//=============================================================================
/**
 *
 *	@file		pkthln_drop.c
 *	@brief		ポケスリング
 *	@author		hosaka genya
 *	@data		2009.01.07
 *
 */
//=============================================================================
#include "common.h"
#include "gflib/assert.h"
#include "strbuf_family.h"
#include "system/wipe.h"
#include "system/clact_tool.h"
#include "system/snd_tool.h"
#include "system/fontproc.h"
#include "gflib/touchpanel.h"

#include "../pkthln_clact.h"

#include "../../pkthln_comm_pack.h"
#include "../../pkthln_obj_common.h"
#include "../../poke_clact.h"
#include "../../poke_clact_move.h"
#include "../../pkthln_poke_act.h"
#include "../../cmn_res.h"
#include "../evt_cats.h"
#include "../event_se_def.h"
#include "../pkthln_evt_info.naix"

//成績作成用
#include "system/msgdata_util.h"
#include "msgdata/msg.naix"
#include "msgdata/msg_pokethlon.h"

#include "drop_define.h"
#include "drop_comm.h"
#include "drop_sub.h"
#include "drop_ai.h"

#include "pkthln_drop.h"

#define DEBUG_PRINT_PUSH		///< 押した力をプリント
//#define DEBUG_PRINT_TOUCH		///< 操作のデバッグ情報

typedef struct DROP_TEAM_DATA_tag * DROP_TEAM_DATA_PTR;
typedef struct RING_POKE_tag * RING_POKE_PTR;

#ifdef _NITRO
		// 構造体が想定のサイズとなっているかチェック
		SDK_COMPILER_ASSERT(sizeof(DROP_COMM_TEAM) == 6);
		SDK_COMPILER_ASSERT(sizeof(DROP_COMM_PARENT) == 36);
		SDK_COMPILER_ASSERT(sizeof(DROP_COMM_CHILD) == 36);
#endif

#ifdef PM_DEBUG
		extern DEBUG_PKTHLN_WORK DebugPkthlnWk;
#endif

#ifdef DEBUG_ONLY_FOR_genya_hosaka
//#define		DEBUG_DUMP_STATE	// 当たった瞬間にデバッグボタンを押していると主人公の状態の記録を表示
#endif

//=============================================================================
/**
 *								定数定義
 */
//=============================================================================
enum
{
	CAP_CHG_X = 16*8,
	CAP_CHG_Y = 2*8,
};

#define SE_PLAYER_MINE		(PLAYER_SE_1)
#define SE_PLAYER_OTHERS	(PLAYER_SE_2)

//--------------------------------------------------------------
///	交代状態
//==============================================================
typedef enum {
	CHG_BALL_NONE = 0,
	CHG_BALL_UP,
	CHG_BALL_DOWN,
}CHG_STATE;

typedef enum {
	CHILD_CHG_NONE = 0,
	CHILD_CHG_UP_END,
	CHILD_CHG_DOWN_END,
}CHILD_CHG_STATE;

enum {
	BTN_ST_PUSH_OK,
	BTN_ST_PUSH_OK_FLASH,
	BTN_ST_PUSH_OK_PUSH,
	BTN_ST_PUSH_NG,
	BTN_ST_PUSH_NG_PUSH,
};

//--------------------------------------------------------------
///	コンバータから取得するパラメータ
//==============================================================
#define DROP_CONV_DATA_MAX (5)
typedef struct DROP_CONV_DATA_tag {
	int Push[ DROP_CONV_DATA_MAX ];			///< 弾き値
	int Power[ DROP_CONV_DATA_MAX ];		///< 威力
	int Stamina[ DROP_CONV_DATA_MAX ];		///< 体力
	int Speed[ DROP_CONV_DATA_MAX ];		///< 移動スピード
	int PressPush[ DROP_CONV_DATA_MAX ];	///< プレス弾き値
	int PressPower[ DROP_CONV_DATA_MAX ];	///< プレス威力
} DROP_CONV_DATA;

//--------------------------------------------------------------
///	状態異常
//==============================================================
typedef enum {
	POKE_STA_ST_NORMAL = 0,
	POKE_STA_ST_SWEAT,
	POKE_STA_ST_PANIC,
} POKE_STA_STATE;

//--------------------------------------------------------------
///	チーム毎のCAP
//==============================================================
typedef enum {
	PCAP_TACKLE = 0,
	PCAP_STA_EFF,
	PCAP_BALL,
	PCAP_SMOKE,
	PCAP_HIT_EFF,
	PCAP_MAX,
};

//--------------------------------------------------------------
///	UI CAP
//==============================================================
typedef enum {
	CAPUI_CANGE,
	CAPUI_MAX,
};

//--------------------------------------------------------------
///	ポケモン毎のパラメータ
//==============================================================
typedef struct DROP_POKE_PARAM_tag {
	fx32 fxPush;		///< 弾き値
	fx32 fxPower;		///< 威力
	fx32 fxStaminaMax;	///< 体力の最大値
	fx32 fxMove;		///< 移動速度
	fx32 fxPressPush;	///< プレス弾き値
	fx32 fxPressPower;	///< プレス威力
	u8 JumpLevel;		///< ジャンプレベル
} DROP_POKE_PARAM;

//--------------------------------------------------------------
///	参加ポケモン管理ワーク
//==============================================================
typedef struct DROP_POKE_tag {
	// [IN]
	POKE_ACT_PTR PokeActPtr;
	DROP_POKE_PARAM* cpp;
	// [PRIVATE]
	fx32 fxStamina;
	u32 ID : 4;
	u32 DummyBit : 28;
} DROP_POKE;

//--------------------------------------------------------------
///	リング上のポケモン用ワーク
//==============================================================
typedef struct RING_POKE_tag {
	// [IN]
	PKTHLN_PTR work;
	DROP_POKE* DropPoke;
	DROP_TEAM_DATA_PTR TeamData;
	DROP_SUB_CNT_PTR cntSub;
	// [PRIVATE]
	RING_POKE_PTR PushPoke;	///< 押された相手のポインタを保持
	POKE_STATE State;
	POKE_STATE HitState;	///< 当てられた瞬間の相手の状態
	POS_DATA TackleTarPos;	///< タックル方向保持
	VecFx32 LastPos;		///< 最終到達地点
	VecFx32 NowPos;			///< 現在地点
	VecFx32 NormDirVec;		///< 移動時に正規化された方向ベクトル(高速化のため保持)
	VecFx32 PushVec;		///< 推進ベクトル
	fx32 fxPosZ;			///< ジャンプ高度
	fx32 fxJumpG;			///< ジャンプ落下速度

	s8 HitWait;				///< ふっとび中、このウェイト中は踏ん張らない
	s8 TackleFrame;			///< タックル用タイマー
	s8 JumpFrame;			///< ジャンプ中のフレームカウンタ
	s8 FallFrame;			///< 落下フレームカウンタ

	s8 PanicFrame;			///< 混乱用カウンタ
	s8 HitStopFrame;		///< ヒットストップ用カウンタ
	s8 ReviveFrame;			///< 復活カウンタ
	u8 BtnSt;				///< 交代ボタンの状態

	u32 Score : 12;			///< ゲームで得た得点
	u32 HitNetID : 2;		///< 衝突時に通信に渡す衝突IDを保持
	u32 HitType : 2;		///< 衝突時に通信に渡す衝突タイプを保持
	u32 Dir : 4;			///< 方向
	u32 NetID : 2;			///< 通信ID
	u32 PokeID : 2;			///< 選択中のポケモンID
	u32 ChgState : 2;		///< 交代シーケンサ
	u32 bChange : 1;		///< 交代フラグ
	u32 bPush : 1;			///< 押されたフラグ
	u32 bPanic : 1;			///< パニック中フラグ
	u32 bJumpPressStart : 1;///< ジャンプ開始フラグ
	u32 bTackleStart : 1;	///< タックル開始フラグ
	u32 bHitSndNormal : 1;	///< ノーマル衝突SE用

	u32 HitSendCnt;			///< 親機が子機にヒット情報を送り続けるSYNCカウンタ
} RING_POKE;

//--------------------------------------------------------------
///	参加チーム管理ワーク
//==============================================================
typedef struct DROP_TEAM_DATA_tag {
	// [PRIVATE]
	POS_DATA TouchPos;		///< タッチした座標
	POS_DATA TrgPos;		///< タッチを開始した座標
	TP_STATE_DROP TpState;
	RING_POKE RingPoke;
	DROP_POKE DropPoke[ ENTRY_MAX ];
	u32 NetID;
} DROP_TEAM_DATA;

//--------------------------------------------------------------
///	親がローカルで使用するデータ
//==============================================================
typedef struct {
	u8 PokeInvalidCollision[ ENTRY_MEMBER_NUM_MAX ][ ENTRY_MEMBER_NUM_MAX ];
	DROP_TEAM_DATA TeamData[ ENTRY_MEMBER_NUM_MAX ];
	int GameTimer;
} DROP_SERVER_WORK;

//--------------------------------------------------------------
///	パレット保存用ワーク
//==============================================================
typedef struct {
	u16 MonsPal[2][16];
} MONS_PAL_DATA;

//--------------------------------------------------------------
///	ポケモン描画ワーク
//==============================================================
typedef struct {
	// [IN]
	POKE_ACT_PTR PokeAct;
	// [PRIVATE]
	CATS_ACT_PTR cap[ PCAP_MAX ];
	POKE_STATE StatePre;
	MONS_PAL_DATA PalData[ ENTRY_MAX ];
	fx32 fxScale;
	s8 JumpFrame;
	s8 FallFrame;
	s8 BallMvCount;
	u8 DummyByte[1];
	u32 PreActCnt : 8;	///< 予備動作用カウンタ
	u32 ChgSeq : 4;
	u32 ChgState : 2;
	u32 PokeID : 2;
	u32 HitNetIDPre : 2;
	u32 bJumping : 1;
	u32 bFalling : 1;
	u32 bFallBitPre : 1;
	u32 bFallChangeSub : 1;
	u32 bChangePre : 1;
	u32 HitTypePre : 2;
	u32 DummyBit : 7;
} POKE_DRAW;

//--------------------------------------------------------------
///	画面揺らしワーク
//==============================================================
typedef struct {
	// [IN]
	CATS_SYS_PTR csp;
	GF_BGL_INI* bgl;
	// [PRIVATE]
	TCB_PTR					tcbShake;
	fx32					ShakeVal;
	fx32					ShakeDec;
	s8						ShakeTimer;
	u8						ShakeFrame;
	u8						ShakeVal_Start;	///< 開始時の揺れ
	BOOL					ShakeVec;
} SHAKE_WORK;

//--------------------------------------------------------------
///	ビューア
//==============================================================
typedef struct {
	// [IN]
	DROP_SUB_CNT_PTR cntSub;
	CATS_SYS_PTR csp;
	CATS_RES_PTR crp;
	// [PRIVATE]
	CATS_ACT_PTR capUI[ CAPUI_MAX ];
	POKE_DRAW drawPoke[ ENTRY_MEMBER_NUM_MAX ];
	u16 ScorePre[ ENTRY_MEMBER_NUM_MAX ];
	u8 ChangeButtonStatePre;	///< 前回の自分のボタン状態
	u8 bHitSndNormalPre : 1;	///< 前回のノーマル衝突SE
	u8 padding : 7;
	u8 DummyByte[2];
} VIEWER_WORK;

//--------------------------------------------------------------
///	メインワーク
//==============================================================
typedef struct PKTHLN_DROP_tag {
	// --- in ---
	HEAPID HeapID;
	// --- gf system pointer ---
	GF_BGL_INI*	bgl;
	CATS_SYS_PTR csp;
	CATS_RES_PTR crp;
	WORDSET* WordSet;
	MSGDATA_MANAGER* MsgMan;
	// -- pokethlon system pointer --
	CMN_RES_CNT_PTR		CmnResCntPtr;
	POKE_ACT_CNT_PTR	PokeActCntPtr;
	// --- private ---
	GF_BGL_BMPWIN		Win;
	DROP_POKE_PARAM		DropPokeParam[ ACT_POKE_MAX ];
	DROP_SUB_CNT_PTR	cntSub;
	DROP_AI_CNT_PTR		cntAI;
	DROP_SERVER_WORK	wkServer;
	VIEWER_WORK			wkViewer;
	SHAKE_WORK			wkShake;
} PKTHLN_DROP;

//=============================================================================
/**
 *								構造体定義
 */
//=============================================================================

//=============================================================================
/**
 *							プロトタイプ宣言
 */
//=============================================================================
// __PROTOTYPE__
static BOOL CheckChangeButton( s16 TpX, s16 TpY );
static BOOL Check_HitVec( VecFx32* vecA, VecFx32* VecB, fx32 fx_range );
static u32 CalcPow2( u32 num );
static void CalcZeroConverge( fx32* p_num, fx32 dec );
static void VEC_ZeroConverge( VecFx32* pVec, fx32 decX, fx32 decY );
static void VEC_Clamp( VecFx32* vec, fx32 max );
static BOOL DROP_Func_Phase1(PKTHLN_PTR work, u8 *seq);
static BOOL DROP_Func_Phase2(PKTHLN_PTR work, u8 *seq);
static BOOL DROP_Func_Phase3(PKTHLN_PTR work, u8 *seq);
PTH_RC PE_DROP_InitFunc( PKTHLN_PTR work, void* pParam );
BOOL PE_DROP_DrawFunc( PKTHLN_PTR work, void* pParam );
BOOL PE_DROP_ResultFunc( PKTHLN_PTR work, void* pParam );
BOOL PE_DROP_EndFunc( PKTHLN_PTR work, void* pParam );
static void SetVramBank(void);
static void CommMain( PKTHLN_PTR work );
static BOOL MainProc( PKTHLN_PTR work );
static void OBJ_Create( PKTHLN_DROP_PTR ptr );
static void OBJ_Delete( PKTHLN_DROP_PTR ptr );
static void OBJ_LoadRes( PKTHLN_DROP_PTR ptr );
static CATS_ACT_PTR OBJ_AddCapMain( CATS_SYS_PTR csp, CATS_RES_PTR crp, s16 px, s16 py, u16 anime, u16 pri );
static void BG_Init( PKTHLN_DROP_PTR ptr );
static void BG_Delete( PKTHLN_DROP_PTR ptr );
static void BG_LoadRes( PKTHLN_DROP_PTR ptr );
static s32 PokePriSort( void* elem1, void* elem2 );
static s32 RankSort( void* elem1, void* elem2 );
static void VIEWER_POKE_Draw( VIEWER_WORK* wk, DROP_COMM_PARENT_PTR parent, PKTHLN_PTR work );
static void VIEWER_Init( VIEWER_WORK* wk, CATS_SYS_PTR csp, CATS_RES_PTR crp, DROP_SUB_CNT_PTR cntSub, POKE_ACT_CNT_PTR cntPokeAct, HEAPID HeapID );
static void VIEWER_Draw( VIEWER_WORK* wk, PKTHLN_PTR work );
static void VIEWER_Exit( VIEWER_WORK* wk );
static void VIEWER_AllObjStop( VIEWER_WORK* wk );
static BOOL CalcPokeHit( RING_POKE* pPokeA, RING_POKE* pPokeB, VecFx32* pIntersect );
static void TeamCnt_Create( PKTHLN_DROP_PTR ptr, PKTHLN_PTR work );
static void TeamCnt_Init( PKTHLN_DROP_PTR ptr, PKTHLN_PTR work );
static void TeamCnt_Delete( PKTHLN_DROP_PTR ptr );
static void TeamCnt_CalcPokeCollision( PKTHLN_PTR work );
static void TeamCntStaminaProc( DROP_TEAM_DATA_PTR pTeamDataHead, PKTHLN_PTR work );
static void TeamCnt_Calc( DROP_TEAM_DATA_PTR pTeamDataHead, PKTHLN_PTR work );
static void TeamCnt_SetCommData( DROP_SERVER_WORK* pServer, PKTHLN_PTR work );
static void Poke_SetAffineVec( POKE_ACT_PTR poke_ptr, BOOL bCenter );
static void Poke_SetState( RING_POKE* pRPoke, POKE_STATE State );
static BOOL Poke_CheckCircle(RING_POKE * poke1, RING_POKE * poke2);
static BOOL Poke_CheckTouchEnable( RING_POKE* pRPoke );
static BOOL Poke_CheckChangeEnable( RING_POKE* pRPoke );
static void Poke_SetLastPos( RING_POKE* pRPoke, const POS_DATA* Pos );
static u8 Poke_CalcDir( VecFx32* norm_dir_vec );
static BOOL Poke_IsHitActive( RING_POKE* pRPoke );
static void Poke_Move( RING_POKE* pRPoke, PKTHLN_PTR work );
static BOOL Poke_CheckFall( RING_POKE* pRPoke );
static void Poke_Revive( RING_POKE* pRPoke );
static void Poke_Reset( RING_POKE* pRPoke );
static void Poke_Calc( RING_POKE* pRPoke, PKTHLN_PTR work );
static void Tackle_Start( RING_POKE* pRPoke, POS_DATA* pTouchPos );
static void Tackle_End( RING_POKE* pRPoke );
static void Tackle_Move( RING_POKE* pRPoke );
static void JumpPress_Start( RING_POKE* pRPoke );
static void JumpPress_End( RING_POKE* pRPoke );
static void HitWait_Start( RING_POKE* pRPoke );
static void Fall_Start( RING_POKE* pRPoke );
static void Change_Start( RING_POKE* pRPoke );
static void Stamina_Dec( RING_POKE* pRPoke, fx32 fxDec );
static void SetDropPokeParam( PKTHLN_PTR work );
static DROP_POKE_PARAM* GetDropPokeParam( PKTHLN_PTR work, u8 NetID, u8 PokeIdx );
static void PokeID_Add( RING_POKE* pRPoke );
static void PlaySe( int inSE, BOOL inMine );
static void CreateBmpWin( PKTHLN_DROP_PTR ptr );
static void SetRecordStr( PKTHLN_PTR work, const int inPoint );
static void SetBtnState( RING_POKE* pRPoke, const TP_STATE_DROP* TpState, const BOOL bValid );

// __PROTOTYPE_END__

//-----------------------------------------------------------------------------
// ■ 関数置き場
// __TempFunc__
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/**
 *	@brief	画面揺らしワーク
 *
 *	@param	TCB_PTR tcb
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TCB_ShakeTask( TCB_PTR tcb, void* work )
{
	SHAKE_WORK* wk = work;
	int vec = ( wk->ShakeVec == 0 ) ? 1 : -1;
	int PosY = (wk->ShakeVal>>FX32_SHIFT) * vec;
	int PosX = PosY/2;

	switch( wk->ShakeTimer % 3 )
	{
		case 0 : PosX *= -1; break;
		case 1 : PosX = 0; break;
		case 2 : PosX *= 1; break;
		default : GF_ASSERT(0);
	}

//	HOSAKA_PRINT("!!!! vec:%d PosX:%d PosY:%d \n", vec, PosX, PosY);

	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_WIN, GF_BGL_SCROLL_X_SET, PosX );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_FRONT, GF_BGL_SCROLL_X_SET, PosX );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_BACK, GF_BGL_SCROLL_X_SET, PosX );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_RSLT, GF_BGL_SCROLL_X_SET, PosX );

	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_WIN, GF_BGL_SCROLL_Y_SET, PosY );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_FRONT, GF_BGL_SCROLL_Y_SET, PosY );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_BACK, GF_BGL_SCROLL_Y_SET, PosY );
	GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_RSLT, GF_BGL_SCROLL_Y_SET, PosY );

	CLACT_U_SetMainSurfaceMatrix(
			CATS_EasyRenderGet( wk->csp ),
			PosX*FX32_ONE, PosY*FX32_ONE );

	wk->ShakeVal -= wk->ShakeDec;

	wk->ShakeVec ^= 1;

	if( ++wk->ShakeTimer >= wk->ShakeFrame )
	{
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_WIN, GF_BGL_SCROLL_X_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_FRONT, GF_BGL_SCROLL_X_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_BACK, GF_BGL_SCROLL_X_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_RSLT, GF_BGL_SCROLL_X_SET, 0 );

		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_WIN, GF_BGL_SCROLL_Y_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_FRONT, GF_BGL_SCROLL_Y_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_BACK, GF_BGL_SCROLL_Y_SET, 0 );
		GF_BGL_ScrollReq( wk->bgl, BG_FRM_M_RSLT, GF_BGL_SCROLL_Y_SET, 0 );

		CLACT_U_SetMainSurfaceMatrix( CATS_EasyRenderGet( wk->csp ), 0, 0 );

		HOSAKA_PRINT("TCB SHAKE END\n");
		wk->tcbShake = NULL;
		TCB_Delete( tcb );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	画面揺らしTCB終了チェック
 *
 *	@param	SHAKE_WORK* wk
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Shake_CheckEnd( SHAKE_WORK* wk )
{
	GF_ASSERT( wk );
	return ( wk->tcbShake == NULL );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	画面揺らし 初期化
 *
 *	@param	SHAKE_WORK* wk
 *	@param	bgl
 *	@param	csp
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Shake_Init( SHAKE_WORK* wk, GF_BGL_INI* bgl, CATS_SYS_PTR csp )
{
	wk->bgl = bgl;
	wk->csp = csp;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	画面揺らしTCB登録関数
 *
 *	@param	PKTHLN_BREAK_PTR ptr
 *	@param	ShakeVal
 *	@param	ShakeFrame
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void StartShakeTask( SHAKE_WORK* wk, u8 ShakeVal, u8 ShakeFrame )
{
	GF_ASSERT( wk->bgl );
	GF_ASSERT( wk->csp );

	// TCB強制終了
	if( wk->tcbShake )
	{
		HOSAKA_PRINT("SHAKE TCB強制終了\n");
		TCB_Delete( wk->tcbShake );
	}

	HOSAKA_PRINT("TCB SHAKE START\n");

	wk->ShakeVec = 0;
	wk->ShakeTimer = 0;
	wk->ShakeFrame = ShakeFrame;
	wk->ShakeVal = ShakeVal * FX32_ONE;
	wk->ShakeVal_Start = ShakeVal;
	wk->ShakeDec = FX_Div( ShakeVal * FX32_ONE, ShakeFrame * FX32_ONE );
	wk->tcbShake = TCB_Add( TCB_ShakeTask, wk, 2 );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	指定座標が交代ボタン範囲内だったらTRUEを返す
 *
 *	@param	TpX
 *	@param	TpY
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL CheckChangeButton( s16 TpX, s16 TpY )
{
	return ( TpX >= 13 * 8 && TpX < 20 * 8 && TpY >= 0 && TpY < 4 * 8 );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	2点の距離判定
 *
 *	@param	VecFx32* vecA			座標A
 *	@param	VecFx32* vecB			座標B
 *	@param	fx_range				半径
 *
 *	@retval	TRUE:接触している
 */
//-----------------------------------------------------------------------------
static BOOL Check_HitVec( VecFx32* vecA, VecFx32* VecB, fx32 fx_range )
{
	fx32 len;
	VecFx32 dir_vec;

	VEC_Subtract( VecB, vecA, &dir_vec );
	len = VEC_Mag(&dir_vec);

	return ( len <= fx_range );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	引数の二乗を返す
 *
 *	@param	u32 num
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u32 CalcPow2( u32 num )
{
	return num * num;
}
//-----------------------------------------------------------------------------
/**
 *	@brief	0に収束させる
 *
 *	@param	fx32* p_num
 *	@param	dec
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void CalcZeroConverge( fx32* p_num, fx32 dec )
{
	if( *p_num == 0 ){ return; }

	if( MATH_IAbs(*p_num) < dec )
	{
		*p_num = 0;
	}
	else
	{
		*p_num -= dec * ( *p_num > 0 ? 1 : -1 );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ベクトルを0に収束させる
 *
 *	@param	VecFx32* pVec
 *	@param	decX
 *	@param	decY
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VEC_ZeroConverge( VecFx32* pVec, fx32 decX, fx32 decY )
{
	CalcZeroConverge( &pVec->x, decX );
	CalcZeroConverge( &pVec->y, decY );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ベクトルのCLAMP
 *
 *	@param	VecFx32* vec
 *	@param	max
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VEC_Clamp( VecFx32* vec, fx32 max )
{
	fx32 min = max*-1;

	if(vec->x > 0) {
		if(vec->x > max){ vec->x = max; }
	} else {
		if(vec->x < min){ vec->x = min; }
	}

	if(vec->y > 0) {
		if(vec->y > max){ vec->y = max; }
	} else {
		if(vec->y < min){ vec->y = min; }
	}
}


//=============================================================================
/**
 *								外部公開関数
 */
//=============================================================================
// --- Event Func ---
static BOOL DROP_Func_Phase1(PKTHLN_PTR work, u8 *seq);
static BOOL DROP_Func_Phase2(PKTHLN_PTR work, u8 *seq);
static BOOL DROP_Func_Phase3(PKTHLN_PTR work, u8 *seq);

PKTHLN_FUNC DropFuncTbl[] = {
	DROP_Func_Phase1,
	DROP_Func_Phase2,
	DROP_Func_Phase3,
};

enum {
	DROP_SEQ_PHASE1,
	DROP_SEQ_PHASE2,
	DROP_SEQ_PHASE3,
};

//-----------------------------------------------------------------------------
/**
 *	@brief	初期化シーケンス関数
 *
 *	@param	work
 *	@param	*seq
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL DROP_Func_Phase1(PKTHLN_PTR work, u8 *seq)
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);

	switch( *seq )
	{
	case 0 :
		// フェードイン
		WIPE_SYS_Start(	WIPE_PATTERN_WMS, WIPE_TYPE_FADEIN,
						WIPE_TYPE_FADEIN, WIPE_FADE_WHITE,
						WIPE_DEF_DIV, WIPE_DEF_SYNC, ptr->HeapID );
		(*seq)++;
		/* FALLTHROW */

	case 1:
		if( WIPE_SYS_EndCheck() )
		{
			(*seq)++;
		}
		break;

	case 2:
		if( PTFrame_CountDown(work) )
		{
			PTFrame_SetNextFuncTblIdx(work, DROP_SEQ_PHASE2);
			HOSAKA_PRINT("CountDown End\n");
		}
		break;

	default : GF_ASSERT(0);
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	メインシーケンス関数
 *
 *	@param	work
 *	@param	*seq
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL DROP_Func_Phase2(PKTHLN_PTR work, u8 *seq)
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);

	switch( *seq )
	{
	case 0:
		CommMain(work);
		if( MainProc(work) )
		{
			(*seq)++;
		}
		break;
	case 1 :
		CommMain(work);
		// リザルト表示
		if( PTFrame_DispEnd(work) )
		{
			// TCB終了待ち
			if( Shake_CheckEnd( &ptr->wkShake ) )
			{
				//フェードアウト
				WIPE_SYS_Start(	WIPE_PATTERN_WMS, WIPE_TYPE_FADEOUT,
							WIPE_TYPE_FADEOUT, WIPE_FADE_BLACK,
							WIPE_DEF_DIV, WIPE_DEF_SYNC, ptr->HeapID );

				PTFrame_SetNextFuncTblIdx(work, DROP_SEQ_PHASE3);
			}
		}
		break;

	default : GF_ASSERT(0);
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	結果シーケンス関数
 *
 *	@param	work		ポケスロンポインタ
 *	@param	*seq		シーケンサ
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL DROP_Func_Phase3(PKTHLN_PTR work, u8 *seq)
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr( work );

	switch( *seq )
	{
	case 0:
		if( WIPE_SYS_EndCheck() )
		{
#if 1
			int netID = PTFrameGetCurrentID(work);

			// 親がスコアを送信
			if( netID == COMM_PARENT_ID )
			{
				int i;

				for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
				{
					u16 Score = ptr->wkServer.TeamData[ i ].RingPoke.Score;
					PTFrame_SetEvtResultOrg( work, i, Score );
					HOSAKA_PRINT("[%d]Score:%d \n",i, Score);
				}
			}
#endif

			HOSAKA_PRINT("Next To ResultFunc \n");
			return TRUE;
		}
		break;

	default : GF_ASSERT(0);

	}

	return FALSE;
}



//-----------------------------------------------------------------------------
/**
 *	@brief	初期化関数
 *
 *	@param	PKTHLN_PTR work
 *	@param	pParam
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
PTH_RC PE_DROP_InitFunc( PKTHLN_PTR work, void* pParam )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	u8 seq = PTFrame_GetSubSeq(work);

	switch( seq )
	{
	case 0 :
		// ヒープ生成
		sys_CreateHeap( HEAPID_POKETHLON, HEAPID_PT_DROP, DROP_HEAP_SIZE );

		// BlankFunc Reset
		sys_VBlankFuncChange( NULL, NULL );	// VBlankセット
		sys_HBlankIntrSet( NULL,NULL );		// HBlankセット

		GF_Disp_GX_VisibleControlInit();
		GF_Disp_GXS_VisibleControlInit();
		GX_SetVisiblePlane( 0 );
		GXS_SetVisiblePlane( 0 );

		// VRAMバンク割り当て
		SetVramBank();

		// 下画面を3D面に
		sys.disp3DSW = DISP_3D_TO_SUB;
		GF_Disp_DispSelect();

		// アプリケーションメモリー
		ptr = PTFrame_AllocEventWork(work, sizeof(PKTHLN_DROP));
		MI_CpuClear8(ptr, sizeof(PKTHLN_DROP));

		// ヒープIDを保持
		ptr->HeapID = HEAPID_PT_DROP;
		ptr->wkServer.GameTimer = GAME_TIME;

		// 次のシーケンスへ
		PTFrame_IncSubSeq(work);
		break;
	case 1:
		// BGLアロケーション
		ptr->bgl = GF_BGL_BglIniAlloc( ptr->HeapID );

		PTFrame_SetRecBGMask(work, BG_FRM_M_RSLT_MASK );

		// BG 初期化
		BG_Init( ptr );

		// BGリソースロード
		BG_LoadRes( ptr );

		// BmiWinを生成
		CreateBmpWin( ptr );

		// CATS 生成
		OBJ_Create( ptr );

		// ポケアクター管理ワーク生成
		TeamCnt_Create( ptr, work );

		// 次のシーケンスへ
		PTFrame_IncSubSeq(work);
		break;
	case 2 :
		if( PokeAct_CheckActSetTask( ptr->PokeActCntPtr ) )
		{
			// CATS管理のOBJリソースをロード
			// ※ポケ、コモンアクターロード後に呼ぶこと
			OBJ_LoadRes( ptr );

			//カウントダウンアクター 初期化
			{
				CLACT_WORK_PTR	*act_adr;
				act_adr = PTFrame_SetCountDownByClSet( work, OBJ_DISTANCE, ptr->CmnResCntPtr, CATS_GetClactSetPtr(ptr->crp) );
				//プライオリティセット
				CLACT_DrawPriorityChg( *act_adr, PRI_COUNT );
			}

			// 次のシーケンスへ
			PTFrame_IncSubSeq(work);
		}
		break;
	case 3:
		{
			u8 NetID = PTFrameGetCurrentID(work);

			// AI生成
			ptr->cntAI = DropAICnt_Create( work, ptr->HeapID );

			// cntSub生成
			ptr->cntSub = DropSub_Create( ptr->csp, ptr->crp, ptr->CmnResCntPtr, ptr->bgl,
										  ptr->MsgMan, ptr->WordSet, NetID, ptr->HeapID, work );

			// 次のシーケンスへ
			PTFrame_IncSubSeq(work);
		}
		break;

	case 4:
		{
			// ポケアクター初期化
			TeamCnt_Init( ptr, work );

			DropSub_Init( ptr->cntSub );

			VIEWER_Init( &ptr->wkViewer, ptr->csp, ptr->crp, ptr->cntSub, ptr->PokeActCntPtr, ptr->HeapID );

			// VBlank Set
			PTFrame_SetDefaultVBlankFunc(ptr->bgl);

			// 画面揺らし 初期化
			Shake_Init( &ptr->wkShake, ptr->bgl, ptr->csp );

			//描画を許可
			PTFrame_SetDraw(work, TRUE);

			// サーバ初期化
			if ( PTFrameGetCurrentID(work) == COMM_PARENT_ID )
			{
				COMM_EVENT_COMMON_DATA* data;
				DROP_COMM_PARENT_PTR parent;

				data = PTFrame_GetEvtComCmnData(work);
				parent = PKTHLN_CommPack_GetData(&data->SendShareData);

				parent->GameTimer = ptr->wkServer.GameTimer;
				TeamCnt_SetCommData( &ptr->wkServer, work );
			}


			// 通信アイコンを起動
			WirelessIconEasyPokethlon( NNS_G2D_VRAM_TYPE_2DSUB );

			//ＯＢＪの表示
			GF_Disp_GX_VisibleControl( GX_PLANEMASK_OBJ, VISIBLE_ON );
			GF_Disp_GXS_VisibleControl( GX_PLANEMASK_OBJ, VISIBLE_ON );

			HOSAKA_PRINT("DROP InitFunc called.\n");

			return PTH_RC_END;
		}
		break;

	default :GF_ASSERT(0);
	}

	return PTH_RC_LOOP;

}

//-----------------------------------------------------------------------------
/**
 *	@brief	描画関数
 *
 *	@param	PKTHLN_PTR work
 *	@param	pParam
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL PE_DROP_DrawFunc( PKTHLN_PTR work, void* pParam )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);

	GF_ASSERT(ptr);

	CATS_Draw( ptr->crp );

	return TRUE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	競技結果計算関数
 *
 *	@param	PKTHLN_PTR work
 *	@param	pParam
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL PE_DROP_ResultFunc( PKTHLN_PTR work, void* pParam )
{
	int i;
	RSLT_PTR pRes;

	pRes = PTFrame_GetResultPtr(work);
	RSLT_Setup( pRes );

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		int rec;

		rec = PTFrame_GetEvtResult(work, i );
		RSLT_SetResult( work, pRes, i, rec, 0, 3, msg_pkthln_rs_K8 );
	}

	// ソート
	RSLT_Sort(pRes, SORT_REC_LARGE);

	// 得点セット
	PTC_CalcPoint(pRes, PKTHLN_EVT_DROP);

	//総得点に加算
	RSLT_AddTotalScore( pRes );

	// 競技得点セット
	RSLT_SetEvtScore( pRes, PKTHLN_EVT_DROP );

	HOSAKA_PRINT("DROP ResultFunc Called.\n");

	return TRUE;

}

//-----------------------------------------------------------------------------
/**
 *	@brief	終了関数
 *
 *	@param	PKTHLN_PTR work
 *	@param	pParam
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL PE_DROP_EndFunc( PKTHLN_PTR work, void* pParam )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);

	DropAICnt_Delete( ptr->cntAI );

	DropSub_Delete( ptr->cntSub );

	VIEWER_Exit( &ptr->wkViewer );

	TeamCnt_Delete( ptr );

	// CATS開放
	OBJ_Delete( ptr );

	// BG開放
	BG_Delete( ptr );

	WirelessIconEasyEnd();

	sys_VBlankFuncChange( NULL, NULL );	// VBlankセット
	sys_HBlankIntrSet( NULL,NULL );		// HBlankセット

	// イベントワーク開放
	PTFrame_FreeEventWork(work);

	// 上下画面を元に戻しておく
	sys.disp3DSW = DISP_3D_TO_MAIN;
	GF_Disp_DispSelect();

	// ヒープ開放
	sys_DeleteHeap( HEAPID_PT_DROP );

	HOSAKA_PRINT("DROP EndFunc Called.\n");

	return TRUE;

}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信データから現在の自分のポケIDを取得
 *
 *	@param	PKTHLN_PTR work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
u8 DROP_COMM_GetMyPokeID( PKTHLN_PTR work )
{
	PKTHLN_DROP_PTR ptr;
	COMM_EVENT_COMMON_DATA* data;
	DROP_COMM_PARENT_PTR parent;
	u8 NetID = PTFrameGetCurrentID(work);

	ptr = PTFrame_GetEventWorkPtr(work);
	data = PTFrame_GetEvtComCmnData(work);
	parent = PKTHLN_CommPack_GetData( &data->ReciveShareData );

	return parent->Team[NetID].Active;

}

//-----------------------------------------------------------------------------
/**
 *
 * 以下の公開関数は親処理からのみアクセス可（AI用)
 *
 */
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの状態を取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *	@param	PokeID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
POKE_STATE Drop_GetPokeState( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr( work );
	return ptr->wkServer.TeamData[NetID].RingPoke.State;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの座標を取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
VecFx32* Drop_GetPokePos( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return &pRPoke->NowPos;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの足元座標を取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void Drop_GetPokePosShadow( PKTHLN_PTR work, u8 NetID, VecFx32* outVec )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	POKE_ACT_PTR poke_act = pRPoke->DropPoke->PokeActPtr;
	int poke_x, poke_y;

	PokeAct_GetShadowPosByTouchPos( poke_act,
		pRPoke->NowPos.x>>FX32_SHIFT, pRPoke->NowPos.y>>FX32_SHIFT,
		&poke_x, &poke_y );

	outVec->x = poke_x * FX32_ONE;
	outVec->y = poke_y * FX32_ONE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの向きを取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
POKE_DIR Drop_GetPokeDir( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return pRPoke->Dir;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンのスタミナを取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
u8 Drop_GetPokeStamina( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return pRPoke->DropPoke->fxStamina >> FX32_SHIFT;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンのスタミナを取得（残り％）
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
u8 Drop_GetPokeStaminaParsent( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	fx32 fxParsent;

	fxParsent = FX_Div( pRPoke->DropPoke->fxStamina, pRPoke->DropPoke->cpp->fxStaminaMax );

	return ( fxParsent * 100 ) >> FX32_SHIFT;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの交代フラグを取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL Drop_GetChangeFlag( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return pRPoke->bChange;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの衝突フラグを取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL Drop_GetPushFlag( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return pRPoke->bPush;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンがタッチ可能状態か判定、結果を取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *	@param	PokeID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
BOOL Drop_CheckPokeEnable( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;
	return Poke_CheckTouchEnable( pRPoke );
}



//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン、指定座標に向かってタックル開始
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *	@param	pTarPos
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void Drop_StartTackle( PKTHLN_PTR work, u8 NetID, POS_DATA* pTarPos )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr( work );

	GF_ASSERT( ptr->wkServer.TeamData[NetID].RingPoke.bPanic == 0 );

	ptr->wkServer.TeamData[NetID].RingPoke.bTackleStart = 1;
	ptr->wkServer.TeamData[NetID].RingPoke.TackleTarPos.X = pTarPos->X;
	ptr->wkServer.TeamData[NetID].RingPoke.TackleTarPos.Y = pTarPos->Y;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン、ジャンププレス開始
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void Drop_StartJumpPress( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr( work );

	GF_ASSERT( ptr->wkServer.TeamData[NetID].RingPoke.bPanic == 0 );
	GF_ASSERT( ptr->wkServer.TeamData[NetID].RingPoke.bChange == 0 );

	ptr->wkServer.TeamData[NetID].RingPoke.bJumpPressStart = 1;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン、交代開始
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void Drop_StartChange( PKTHLN_PTR work, u8 NetID )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr( work );
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;

	Change_Start( pRPoke );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン、指定座標に移動開始
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *	@param	pos
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
void Drop_SetMovePos( PKTHLN_PTR work, u8 NetID, const POS_DATA* Pos )
{
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);
	RING_POKE* pRPoke = &ptr->wkServer.TeamData[NetID].RingPoke;

//	HOSAKA_PRINT("SetPos {%d,%d} \n", Pos->X, Pos->Y);

	Poke_SetLastPos( pRPoke, Pos );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	正規化されたベクトルからポケモンの向きを取得
 *
 *	@param	VecFx32* norm_dir_vec
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
POKE_DIR Drop_CalcPokeDir( VecFx32* norm_dir_vec )
{
	return Poke_CalcDir( norm_dir_vec );
}

//=============================================================================
/**
 *								static関数
 */
//=============================================================================

//--------------------------------------------------------------------------------------------
/**
 * VRAM設定		ポケスロンでは、ポケモン表示だけで0x30000必要となる。
 * VRAM_AB(0x40000)でないとはいらん
 *
 * @param	none
 *
 * @return	none
 */
//--------------------------------------------------------------------------------------------
static void SetVramBank(void)
{
	GF_BGL_DISPVRAM tbl = {
		GX_VRAM_BG_64_E,				// メイン2DエンジンのBG
		GX_VRAM_BGEXTPLTT_NONE,			// メイン2DエンジンのBG拡張パレット

		GX_VRAM_SUB_BG_128_C,			// サブ2DエンジンのBG
		GX_VRAM_SUB_BGEXTPLTT_NONE,		// サブ2DエンジンのBG拡張パレット

		GX_VRAM_OBJ_256_AB,				// メイン2DエンジンのOBJ
		GX_VRAM_OBJEXTPLTT_NONE,		// メイン2DエンジンのOBJ拡張パレット

		GX_VRAM_SUB_OBJ_16_I,			// サブ2DエンジンのOBJ
		GX_VRAM_SUB_OBJEXTPLTT_NONE,	// サブ2DエンジンのOBJ拡張パレット

		GX_VRAM_TEX_NONE,				// テクスチャイメージスロット
		GX_VRAM_TEXPLTT_NONE			// テクスチャパレットスロット
	};
	GF_Disp_SetBank( &tbl );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信処理メイン
 *
 *	@param	PKTHLN_PTR work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void CommMain( PKTHLN_PTR work )
{
	int i;
	PKTHLN_DROP_PTR ptr;
	COMM_EVENT_COMMON_DATA* data;
	DROP_COMM_PARENT_PTR parent;

	ptr = PTFrame_GetEventWorkPtr(work);
	data = PTFrame_GetEvtComCmnData(work);
	parent = PKTHLN_CommPack_GetData( &data->SendShareData );

	// 親処理
	if( PTFrameGetCurrentID(work) == COMM_PARENT_ID )
	{
		// 終了フラグが立っていれば処理を打ち切る
		if( parent->EndFlag )
		{
			// 通信データセット
			TeamCnt_SetCommData( &ptr->wkServer, work );
			return;
		}

		// 時間経過
		if( ptr->wkServer.GameTimer > 0 )
		{
#ifdef PM_DEBUG
			if( DebugPkthlnWk.TimeInfinity == 0 )
#endif
			{
				ptr->wkServer.GameTimer--;
			}

			parent->GameTimer = ptr->wkServer.GameTimer;
		}

		//親データは直接レシーバにセット
		{
			DROP_COMM_CHILD_PTR child;
			DROP_COMM_CHILD_PTR child_rec;

			child_rec = PKTHLN_CommPack_GetData(&data->ReciveData[0]);
			child = PKTHLN_CommPack_GetData(&data->SendData);
			(*child_rec) = (*child);
		}

		// 親機 レシーバー集計
		for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
		{
			DROP_COMM_CHILD_PTR child_rec;
			DROP_TEAM_DATA_PTR TeamData;
			TP_STATE_DROP* TpState;
			RING_POKE* pRPoke;

			child_rec = PKTHLN_CommPack_GetData(&data->ReciveData[i]);
			TeamData = &ptr->wkServer.TeamData[i];
			TpState = &TeamData->TpState;
			pRPoke = &TeamData->RingPoke;

			// タッチ状況を整理
			if( child_rec->TouchVaild )
			{
				if( TpState->Trg && TpState->Cont )
				{
					// トリガフラグOFF
					TpState->Trg = FALSE;
				}
				else if( TpState->Trg == FALSE && TpState->Cont == FALSE )
				{
					// トリガフラグON
					TpState->Trg = TRUE;
					TpState->Cont = TRUE;

					// 初回タッチ座標を保持
					TeamData->TrgPos.X = child_rec->TpX;
					TeamData->TrgPos.Y = child_rec->TpY;
#ifdef PM_DEBUG
#ifdef DEBUG_PRINT_TOUCH
					HOSAKA_PRINT("trg{%d,%d} \n", TeamData->TrgPos.X,TeamData->TrgPos.Y );
#endif
#endif

				}

				TeamData->TouchPos.X = child_rec->TpX;
				TeamData->TouchPos.Y = child_rec->TpY;

//				HOSAKA_PRINT("[%d] TpCont{%d,%d} cnt:%d \n",i,child_rec->TpX,child_rec->TpY,TpState->ContCounter);
				TpState->ContCounter++;
			}
			else if( TpState->Cont )
			{
				TpState->ReleaseCount = TpState->ContCounter;
				TpState->ContCounter = 0;
				TpState->Trg = FALSE;
				TpState->Cont = FALSE;

#ifdef PM_DEBUG
#ifdef DEBUG_PRINT_TOUCH
				HOSAKA_PRINT("release{%d,%d} \n", TeamData->TouchPos.X,	TeamData->TouchPos.Y );
#endif
#endif
			}

#if 0
//#ifdef DEBUG_ONLY_FOR_genya_hosaka
			// チェンジにかかったフレーム数をデバッグ表示
			{
				static int cntChange[ ENTRY_MEMBER_NUM_MAX ] = {0};

				if( pRPoke->bChange )
				{
					cntChange[i]++;
				}
				else
				{
					if( cntChange[i] > 0 )
					{
						OS_TPrintf("[%d] change frame = %d \n", i, cntChange[i] );
						cntChange[i] = 0;
					}
				}
			}
#endif

			// 交代ボタンの状態遷移
			SetBtnState(pRPoke, TpState, CheckChangeButton( TeamData->TouchPos.X, TeamData->TouchPos.Y ) );

			//交代中
			if (pRPoke->bChange)
			{
				//子機からの反応を取得
				if ( (pRPoke->ChgState == CHG_BALL_UP)&&(child_rec->ChgState == CHILD_CHG_UP_END) )
				{
					HOSAKA_PRINT("%dのボール打ち上げが終了\n", i);
					//ポケ交代
					PokeID_Add( pRPoke );
					// リセット
					Poke_Reset( pRPoke );
					//ボール落下アニメに移行する
					pRPoke->ChgState = CHG_BALL_DOWN;
					HOSAKA_PRINT("%dの次のポケはインデックス%d\n",i, pRPoke->PokeID);
				}
				else if( (pRPoke->ChgState == CHG_BALL_DOWN)&&(child_rec->ChgState == CHILD_CHG_DOWN_END) )
				{
					HOSAKA_PRINT("%dのボール落下が終了\n");
					//ポケ交代終了
					pRPoke->bChange = 0;
					pRPoke->ChgState = CHG_BALL_NONE;
					Poke_SetState( pRPoke, POKE_ST_REVIVE );
					HOSAKA_PRINT("[%d] Change END \n", i);
				}
			}
		}

		{
#ifdef PM_DEBUG
			if( DebugPkthlnWk.AI_Stop == 0 )
#endif
			{
				// AI処理
				DropAICnt_Proc( ptr->cntAI );
			}
		}
		// ポケモン処理
		TeamCnt_Calc( ptr->wkServer.TeamData, work );

		// 終了判定
		if( parent->GameTimer <= 0)
		{
			parent->EndFlag = 1;
		}

#ifdef PM_DEBUG
		// Xで強制終了(親が終了するとみんな終了)
		if( sys.trg & PAD_BUTTON_X )
		{
			parent->EndFlag = 1;
		}
#endif

		// 通信データセット
		TeamCnt_SetCommData( &ptr->wkServer, work );

	} // 親機処理終了

	// 描画処理
	VIEWER_Draw( &ptr->wkViewer, work );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	レコードコールバック関数
 *
 *	@param	work
 *
 *	@retval	none
 */
//-----------------------------------------------------------------------------
static void RecCallBack(PKTHLN_PTR work)
{
	COMM_EVENT_COMMON_DATA * data;
	DROP_COMM_PARENT_PTR parent;
	PKTHLN_DROP_PTR ptr;

	data = PTFrame_GetEvtComCmnData(work);
	parent = PKTHLN_CommPack_GetData(&data->ReciveShareData);
	ptr = PTFrame_GetEventWorkPtr(work);

	//ニューレコード用にレコードをセット
	{
		u8 point;
		point = DropSub_GetDispScore( ptr->cntSub );
		//フレームワークにニューレコードチェック用のレコード値をセット
		PTFrame_SetCheckRecord(work, point);
		SetRecordStr(work, point);
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	主処理
 *
 *	@param	PKTHLN_PTR work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL MainProc( PKTHLN_PTR work )
{
	PKTHLN_DROP_PTR ptr;
	COMM_EVENT_COMMON_DATA* data;
	DROP_COMM_CHILD_PTR child_data;

	ptr = PTFrame_GetEventWorkPtr(work);
	data = PTFrame_GetEvtComCmnData(work);
	child_data = PKTHLN_CommPack_GetData(&data->SendData);

	// 終了判定
	{
		DROP_COMM_PARENT_PTR parent = PKTHLN_CommPack_GetData(&data->ReciveShareData);

		if( parent->EndFlag == 1 )
		{
			// 全てのOBJアニメ停止
			VIEWER_AllObjStop( &ptr->wkViewer );
			PokeAct_AllAnmStop( ptr->PokeActCntPtr, 1 );
			DropSub_Finish( ptr->cntSub );

			PTFrame_SetRecordCallBack( work, RecCallBack );
//			SetRecordStr( work, DropSub_GetDispScore( ptr->cntSub ) );
			return TRUE;
		}
	}

	if( GF_TP_GetTrg() )
	{
		// AP:タッチ回数
		PTFrame_AddActionPoint( work, PTFrameGetCurrentID(work), ENTRY_MAX, ADD_TYPE_TOUCH, 1 );
	}

	if( GF_TP_GetCont() )
	{
		// 通信データに座標をセット
		child_data->TpX = sys.tp_x;
		child_data->TpY = sys.tp_y;
		child_data->TouchVaild = TRUE;
//		HOSAKA_PRINT("[%d]Send Touch \n", PTFrameGetCurrentID(work) );
	}
	else
	{
		child_data->TouchVaild = FALSE;
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	CATSシステム生成
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void OBJ_Create( PKTHLN_DROP_PTR ptr )
{
	TCATS_RESOURCE_NUM_LIST crnl = { CHR_ID_MAX, PAL_ID_MAX, CEL_ID_MAX, ANM_ID_MAX, 0 };

	TCATS_OAM_INIT	coi = {
		0, 126,
		0, 32,
		1, 126,
		0, 32,
	};

	TCATS_CHAR_MANAGER_MAKE ccmm = {
		0,	// dummy
		1024*256,
		1024*16,
		GX_OBJVRAMMODE_CHAR_1D_256K,
		GX_OBJVRAMMODE_CHAR_1D_256K,
	};
	ccmm.num = OBJ_ID_MAX;

	ptr->csp = CATS_AllocMemory( ptr->HeapID );
	ptr->crp = CATS_ResourceCreate( ptr->csp );

	CATS_SystemInit( ptr->csp, &coi, &ccmm, 32 );
	CATS_ClactSetInit( ptr->csp, ptr->crp, CAP_ALL_NUM);
	CATS_ResourceManagerInit( ptr->csp, ptr->crp, &crnl );

	//サブサーフェースの距離を設定
	CLACT_U_SetSubSurfaceMatrix( CATS_EasyRenderGet( ptr->csp ), 0, OBJ_DISTANCE );

	// 共通リソース
	ptr->CmnResCntPtr = CmnRes_Init( ptr->HeapID, CMN_RES_ALL_BIT|CMN_RES_PEN_M_BIT|CMN_RES_F2X2_BIT, NNS_G2D_VRAM_TYPE_2DMAIN );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	CATSシステム開放
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void OBJ_Delete( PKTHLN_DROP_PTR ptr )
{

	CmnRes_Free( ptr->CmnResCntPtr );
	CATS_ResourceDestructor_S( ptr->csp, ptr->crp );
	CATS_FreeMemory( ptr->csp );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	OBJ用リソースのロード
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void OBJ_LoadRes( PKTHLN_DROP_PTR ptr )
{
	int i;
	CATS_SYS_PTR csp = ptr->csp;
	CATS_RES_PTR crp = ptr->crp;
	NNS_G2D_VRAM_TYPE vram_type;

	GF_ASSERT(csp);
	GF_ASSERT(crp);

	//===========================================================
	// Main OBJ
	//===========================================================
	vram_type = NNS_G2D_VRAM_TYPE_2DMAIN;

	CATS_LoadResourceCharArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_main_lz_NCGR,
			TRUE, vram_type, CHR_ID_MAIN );

	CATS_LoadResourcePlttArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_main_NCLR,
			0, 2, vram_type, PAL_ID_MAIN );

	CATS_LoadResourceCellArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_main_lz_NCER,
			TRUE, CEL_ID_MAIN );

	CATS_LoadResourceCellAnmArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_main_lz_NANR,
			TRUE, ANM_ID_MAIN );

	//===========================================================
	// Sub OBJ
	//===========================================================
	vram_type = NNS_G2D_VRAM_TYPE_2DSUB;

	CATS_LoadResourceCharArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_sub_lz_NCGR,
			TRUE, vram_type, CHR_ID_SUB );

	CATS_LoadResourcePlttArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_sub_NCLR,
			0, 2, vram_type, PAL_ID_SUB );

	CATS_LoadResourceCellArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_sub_lz_NCER,
			TRUE, CEL_ID_SUB );

	CATS_LoadResourceCellAnmArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_drop_obj_sub_lz_NANR,
			TRUE, ANM_ID_SUB );

	//===========================================================
	// POKEGRA
	//===========================================================
	vram_type = NNS_G2D_VRAM_TYPE_2DSUB;

#if 1
	for( i=0; i<OBJ_POKEGRA_S_MAX; i++ )
	{
		CATS_LoadResourceCharArc(
				csp, crp, ARC_PKTHEV_DROP,
				NARC_pkthev_drop_pokegra_dmy_vram256_lz_NCGR,
				TRUE, vram_type, CHR_ID_S_POKEGRA_1 + i );

		CATS_LoadResourcePlttArc(
				csp, crp, ARC_PKTHEV_DROP,
				NARC_pkthev_drop_pokegra_dmy_vram256_NCLR,
				0, 1, vram_type, PAL_ID_S_POKEGRA_1 + i );
	}

	CATS_LoadResourceCellArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_pokegra_dmy_vram256_lz_NCER,
			TRUE, CEL_ID_POKEGRA );

	CATS_LoadResourceCellAnmArc( csp, crp, ARC_PKTHEV_DROP,
			NARC_pkthev_drop_pokegra_dmy_vram256_lz_NANR,
			TRUE, ANM_ID_POKEGRA );
#endif
}

//-----------------------------------------------------------------------------
/**
 *	@brief	汎用OBJ追加 MAIN
 *
 *	@param	CATS_SYS_PTR csp
 *	@param	crp
 *	@param	px
 *	@param	py
 *	@param	anime
 *	@param	pri
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static CATS_ACT_PTR OBJ_AddCapMain( CATS_SYS_PTR csp, CATS_RES_PTR crp, s16 px, s16 py, u16 anime, u16 pri )
{
	TCATS_OBJECT_ADD_PARAM_S prm = {
		0, 0, 0,
		0, 0, TCATS_ADD_S_PAL_AUTO, NNS_G2D_VRAM_TYPE_2DMAIN,
		{ CHR_ID_MAIN, PAL_ID_MAIN, CEL_ID_MAIN, ANM_ID_MAIN },
		2, 0,
	};

	CATS_ACT_PTR cap;

	GF_ASSERT(csp);
	GF_ASSERT(crp);

	prm.x = px;
	prm.y = py;
	prm.anm = anime;
	prm.pri = pri;

	cap = CATS_ObjectAdd_S_SubDistance( csp, crp, &prm, OBJ_DISTANCE );
	CATS_ObjectAutoAnimeSetCap( cap, TRUE );

	return cap;
}


//-----------------------------------------------------------------------------
/**
 *	@brief	BG初期化
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BG_Init( PKTHLN_DROP_PTR ptr )
{
	GF_BGL_INI* bgl = ptr->bgl;
	HEAPID HeapID = ptr->HeapID;

	{	/* BG SYSTEM */
		GF_BGL_SYS_HEADER BGsys_data = {
			GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BGMODE_0, GX_BG0_AS_2D,
		};
		GF_BGL_InitBG( &BGsys_data );
	}

	{
		int i;
		GF_BGL_BGCNT_HEADER bg_data[ BG_USE_NUM ] = {
			// -- MAIN --
			{	// BG_0 WIN + TEXT
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0xf800,GX_BG_CHARBASE_0x00000,GX_BG_EXTPLTT_01,
				1,0,0,FALSE
			},
			{	// BG_1 リング手前
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0xf000,GX_BG_CHARBASE_0x04000,GX_BG_EXTPLTT_01,
				2,0,0,FALSE
			},
			{	// BG_2 リング本体
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0xe800,GX_BG_CHARBASE_0x08000,GX_BG_EXTPLTT_01,
				3,0,0,FALSE
			},
			{	// BG_3 リザルト
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0xe000,GX_BG_CHARBASE_0x0c000,GX_BG_EXTPLTT_01,
				0,0,0,FALSE
			},

			// -- SUB --
			{	// BG_0 土台
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0x0000,GX_BG_CHARBASE_0x04000,GX_BG_EXTPLTT_01,
				3,0,0,FALSE
			},
			{	// BG_1 テキスト
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0x0800,GX_BG_CHARBASE_0x08000,GX_BG_EXTPLTT_01,
				0,0,0,FALSE
			},
			{	// BG_2 SCORE
				0,0,0x800,0,GF_BGL_SCRSIZ_256x256,GX_BG_COLORMODE_16,
				GX_BG_SCRBASE_0x1000,GX_BG_CHARBASE_0x0c000,GX_BG_EXTPLTT_01,
				1,0,0,FALSE
			},
		};

		for( i=0; i<BG_USE_NUM; i++ )
		{
			GF_BGL_BGControlSet( bgl, c_BG_UseList[i], &(bg_data[i]), GF_BGL_MODE_TEXT );
			GF_BGL_ScrClear( bgl, c_BG_UseList[i] );
			GF_BGL_ClearCharSet( c_BG_UseList[i], 32, 0, HeapID );
		}
	}

	ptr->MsgMan = MSGMAN_Create(MSGMAN_TYPE_DIRECT, ARC_MSG, NARC_msg_pokethlon_dat, ptr->HeapID );
	ptr->WordSet = WORDSET_Create( ptr->HeapID );

	//タッチフォントロード
	FontProc_LoadFont(FONT_TOUCH, ptr->HeapID);

	//レコードＢＧ非表示
	GF_Disp_GX_VisibleControl( BG_FRM_M_RSLT_MASK, VISIBLE_OFF );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	BG削除
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BG_Delete( PKTHLN_DROP_PTR ptr )
{
	int i;

	// BMPWIN削除
	GF_BGL_BmpWinDel( &ptr->Win );

	WORDSET_Delete( ptr->WordSet );
	MSGMAN_Delete( ptr->MsgMan );

	// BGコントーラー開放
	for( i=0; i<BG_USE_NUM; i++ )
	{
		GF_BGL_BGControlExit( ptr->bgl, c_BG_UseList[i] );
	}

	//タッチフォントアンロード
	FontProc_UnloadFont(FONT_TOUCH);

	// BGL開放
	sys_FreeMemoryEz( ptr->bgl );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	BGリソースロード
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BG_LoadRes( PKTHLN_DROP_PTR ptr )
{
	// ++ 下画面 (Main) ++
	//キャラ
	ArcUtil_BgCharSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main01_lz_NCGR,
						ptr->bgl, BG_FRM_M_WIN, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_BgCharSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main02_lz_NCGR,
						ptr->bgl, BG_FRM_M_FRONT, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_BgCharSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main03_lz_NCGR,
						ptr->bgl, BG_FRM_M_BACK, 0, 0, TRUE, ptr->HeapID);

	//スクリーン
	ArcUtil_ScrnSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main01_lz_NSCR,
						ptr->bgl, BG_FRM_M_WIN, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_ScrnSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main02_lz_NSCR,
						ptr->bgl, BG_FRM_M_FRONT, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_ScrnSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main03_lz_NSCR,
						ptr->bgl, BG_FRM_M_BACK, 0, 0, TRUE, ptr->HeapID);

	//パレット
	ArcUtil_PalSet( ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_main_NCLR,
						PALTYPE_MAIN_BG, 0, 0, ptr->HeapID );

	//システム用フォントパレット転送
	SystemFontPaletteLoad(PALTYPE_MAIN_BG, MAIN_FONT_PAL*0x20, ptr->HeapID);

	// ++ 上画面 (Sub) ++

	//キャラ
	ArcUtil_BgCharSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_sub01_lz_NCGR,
						ptr->bgl, BG_FRM_S_SCORE, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_BgCharSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_sub02_lz_NCGR,
						ptr->bgl, BG_FRM_S_BASE, 0, 0, TRUE, ptr->HeapID);

	//スクリーン
	ArcUtil_ScrnSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_sub02_lz_NSCR,
						ptr->bgl, BG_FRM_S_BASE, 0, 0, TRUE, ptr->HeapID);

	ArcUtil_ScrnSet(ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_sub01_lz_NSCR,
						ptr->bgl, BG_FRM_S_SCORE, 0, 0, TRUE, ptr->HeapID);

	//パレット
	ArcUtil_PalSet( ARC_PKTHEV_DROP, NARC_pkthev_drop_drop_bg_sub_NCLR,
						PALTYPE_SUB_BG, 0, 0, ptr->HeapID );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代 ボール上昇シーケンス
 *
 *	@param	POKE_DRAW* pPokeDraw
 *	@param	pos
 *	@param	cntSub
 *	@param	NetID
 *	@param	outChildData
 *	@param	req_npc_mv
 *	@param	bReqSubDemo
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BallUpAnm( POKE_DRAW* pPokeDraw, POS_DATA* pos, DROP_SUB_CNT_PTR cntSub, u8 NetID, DROP_COMM_CHILD_PTR outChildData, BOOL req_npc_mv, BOOL bReqSubDemo )
{
	switch( pPokeDraw->ChgSeq )
	{
		case 0 :
			{
				// 煙アニメ開始
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_SMOKE ], pos->X, pos->Y, OBJ_DISTANCE );
				CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_SMOKE ], CATS_ENABLE_TRUE );
				CATS_ObjectAnimeReStartCap( pPokeDraw->cap[ PCAP_SMOKE ] );
				// ボール出現
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_BALL ], pos->X, pos->Y, OBJ_DISTANCE );
				CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_BALL ], CATS_ENABLE_TRUE );
				// ポケモンを隠す
				PokeAct_DispCnt( pPokeDraw->PokeAct, FALSE );
				// カウンタ初期化
				pPokeDraw->BallMvCount = 0;

				// 打ち上げSE
				PlaySe( SE_RUN_CHANGE_OUT1, bReqSubDemo );
			}
			pPokeDraw->ChgSeq++;
			break;
		case 1 :
			{
				s16 px, py;
				// ボール移動
				CATS_ObjectPosGetCap( pPokeDraw->cap[ PCAP_BALL ], &px, &py );
				py -= BALL_MOVE_SPEED;
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_BALL ], px, py, OBJ_DISTANCE );
				pPokeDraw->BallMvCount++;

				if( pPokeDraw->BallMvCount >= BALL_MOVE_COUNT )
				{
					CATS_ObjectEnableCap( pPokeDraw->cap[PCAP_BALL], CATS_ENABLE_FALSE );
					pPokeDraw->ChgSeq++;
				}
			}
			break;
		case 2 :
			// 自分のポケなら上画面の表示変更デモ
			if( bReqSubDemo )
			{
				if( DropSub_ChangePokeMain( cntSub, NetID, pPokeDraw->PokeID ) )
				{
					// ボールの上昇を親に通達
					outChildData->ChgState = CHILD_CHG_UP_END;
					pPokeDraw->ChgSeq++;
				}
			}
			else
			{
				if( req_npc_mv )
				{
					// ダミースライド関数
					if( DropSub_ChangePokeNpc( cntSub, NetID, pPokeDraw->PokeID ) )
					{
						// ボールの上昇を親に通達
						outChildData->ChgState = CHILD_CHG_UP_END;
						pPokeDraw->ChgSeq++;
					}
				}
				// 自分とは無関係のポケ
				else
				{
					pPokeDraw->ChgSeq++;
				}
			}
			break;
		case 3 :
			// 待機
			break;
		default :
			GF_ASSERT(0);
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代 ボール落下シーケンス
 *
 *	@param	POKE_DRAW* pPokeDraw
 *	@param	pos
 *	@param	cntSub
 *	@param	NetID
 *	@param	outChildData
 *	@param	bReqSend
 *	@param	bReqSubDemo
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void BallDownAnm( POKE_DRAW* pPokeDraw, POS_DATA* pos, DROP_SUB_CNT_PTR cntSub, u8 NetID, DROP_COMM_CHILD_PTR outChildData, BOOL bReqSend, BOOL bReqSubDemo )
{
	switch( pPokeDraw->ChgSeq )
	{
		case 0 :
			{
				s16 PosY = pos->Y;
				// ボールの位置を画面上にセット
				PosY -= BALL_MOVE_SPEED * BALL_MOVE_COUNT;
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_BALL ], pos->X, PosY, OBJ_DISTANCE );
				CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_BALL ], CATS_ENABLE_TRUE );
				// カウンタ初期化
				pPokeDraw->BallMvCount = 0;

				// 落下SE
				PlaySe(SE_RUN_CHANGE_IN1, bReqSubDemo);
			}
			pPokeDraw->ChgSeq++;
			break;
		case 1 :
			{
				s16 px, py;
				// ボール移動
				CATS_ObjectPosGetCap( pPokeDraw->cap[PCAP_BALL], &px, &py );
				py += BALL_MOVE_SPEED;
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[PCAP_BALL], px, py, OBJ_DISTANCE );

				pPokeDraw->BallMvCount++;
				if( pPokeDraw->BallMvCount >= BALL_MOVE_COUNT )
				{
					pPokeDraw->ChgSeq++;
				}
			}
			break;
		case 2 :
			if( bReqSend )
			{
				// ボール落下終了を親に通達
				outChildData->ChgState = CHILD_CHG_DOWN_END;
			}
			pPokeDraw->ChgSeq++;
			break;
		case 3 :
			// 待機
			break;
		default :
			GF_ASSERT(0);
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代 終了処理
 *
 *	@param	POKE_DRAW* pPokeDraw
 *	@param	POS_DATA* pos
 *	@param	outChildData
 *	@param	bReqSend
 *	@param	bReqSubDemo
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void ChangeEndAnm( POKE_DRAW* pPokeDraw, POS_DATA* pos, DROP_COMM_CHILD_PTR outChildData, BOOL bReqSend, BOOL bReqSubDemo )
{
	HOSAKA_PRINT("ポケ再動エフェクト\n");

	// 次のポケモンアクターを表示
	PokeAct_DispCnt( pPokeDraw->PokeAct, TRUE );
	// 煙
	CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_SMOKE ], pos->X, pos->Y, OBJ_DISTANCE );
	CATS_ObjectAnimeReStartCap( pPokeDraw->cap[ PCAP_SMOKE ] );
	// ボール消す
	CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_BALL ], CATS_ENABLE_FALSE );

	// 煙再生SE
	PlaySe( SE_RUN_CHANGE_IN2, bReqSubDemo );

	if( bReqSend )
	{
		outChildData->ChgState = CHILD_CHG_NONE;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代デモ
 *
 *	@param	DROP_COMM_TEAM* pCommPoke
 *	@param	pPokeDraw
 *	@param	cntSub
 *	@param	work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void ChangeDemo( DROP_COMM_TEAM* pCommPoke, POKE_DRAW* pPokeDraw, DROP_SUB_CNT_PTR cntSub, PKTHLN_PTR work, u8 NetID )
{
	COMM_EVENT_COMMON_DATA* commData;
	DROP_COMM_CHILD_PTR child_data;
	POS_DATA pos;
	BOOL npc;
	BOOL b_parent;
	BOOL req_send;
	BOOL req_sub_demo;
	BOOL npc_mv;			///< 自分が親で、デモ要求がNPC
	BOOL bFirst = 0;

	commData = PTFrame_GetEvtComCmnData(work);

	pos.X = pCommPoke->PokeX;
	pos.Y = pCommPoke->PokeY;

	//NPC判定
	if ( PTFrame_GetPlayerNum(work) <= NetID ){
		npc = TRUE;
		child_data = PKTHLN_CommPack_GetData(&commData->ReciveData[NetID]);
	}else{
		npc = FALSE;
		child_data = PKTHLN_CommPack_GetData(&commData->SendData);
	}

	b_parent = (PTFrameGetCurrentID(work) == COMM_PARENT_ID);
	//自分が親で、デモ要求がＮＰＣ
	npc_mv = ( b_parent && npc );
	//上画面演出フラグ
	req_sub_demo = ( PTFrameGetCurrentID(work) == NetID );
	//ＮＰＣ動作要求かサブ画面デモ要求があった場合は、親に終了を送る必要がある
	req_send = npc_mv | req_sub_demo;

	// ステート変更検出
	if( pCommPoke->ChgState != pPokeDraw->ChgState )
	{
		// 初期化
		pPokeDraw->ChgSeq = 0;
		pPokeDraw->ChgState = pCommPoke->ChgState;
		bFirst = 1;
	}

	switch( pPokeDraw->ChgState )
	{
		case CHG_BALL_UP :
			// 初回なら上画面操作
			if( bFirst )
			{
				DropSub_SetPokeIconOrBall( cntSub, NetID, pCommPoke->Active, TRUE );
			}
			BallUpAnm( pPokeDraw, &pos, cntSub, NetID, child_data, npc_mv, req_sub_demo );
			break;

		case CHG_BALL_DOWN :
			BallDownAnm( pPokeDraw, &pos, cntSub, NetID, child_data, req_send, req_sub_demo );
			break;

		case CHG_BALL_NONE:
			//初検出ならば 交代終了
			if( bFirst )
			{
				ChangeEndAnm( pPokeDraw, &pos, child_data, req_send, req_sub_demo );
				DropSub_SetPokeIconOrBall( cntSub, NetID, pCommPoke->Active, FALSE );
			}
			break;
	}
}

//--------------------------------------------------------------
///	Y軸ソート用データ
//==============================================================
typedef struct {
	u8 PosY;
	POKE_ACT_PTR act;
} POKE_PRI_SORT;

//-----------------------------------------------------------------------------
/**
 *	@brief	Y軸ソート用の比較関数
 *
 *	@param	void* elem1
 *	@param	elem2
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static s32 PokePriSort( void* elem1, void* elem2 )
{
	POKE_PRI_SORT* p1 = (POKE_PRI_SORT*)elem1;
	POKE_PRI_SORT* p2 = (POKE_PRI_SORT*)elem2;

	if( p1->PosY == p2->PosY )
	{
		return 0;
	}
	else if( p1->PosY < p2->PosY )
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ランクソート用の比較関数
 *
 *	@param	void* elem1
 *	@param	elem2
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static s32 RankSort( void* elem1, void* elem2 )
{
	RANK_DATA* p1 = (RANK_DATA*)elem1;
	RANK_DATA* p2 = (RANK_DATA*)elem2;

	if( p1->Score == p2->Score )
	{
		return 0;
	}
	else if( p1->Score < p2->Score )
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン描画
 *
 *	@param	COMM_VIEWER_WORK* wk
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VIEWER_POKE_Draw( VIEWER_WORK* wk, DROP_COMM_PARENT_PTR parent, PKTHLN_PTR work )
{
	enum{ COMM_FALL_SYNC = 6, };

	int i;
	PKTHLN_DROP_PTR ptr;
	POKE_PRI_SORT sort[ENTRY_MEMBER_NUM_MAX] = {0};

	ptr = PTFrame_GetEventWorkPtr(work);

	// ノーマル衝突SE
	if( parent->bHitSndNormal && wk->bHitSndNormalPre == 0 )
	{
		Snd_SePlay( SEQ_SE_GS_IDOUHIT );
	}
	wk->bHitSndNormalPre = parent->bHitSndNormal;

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		DROP_COMM_TEAM* pCommPoke;
		POKE_DRAW* pPokeDraw;
		POKE_ACT_PTR poke_act;
		u8 ActiveID;
		u8 PokeID;
		BOOL npc;
		BOOL b_parent;
		BOOL npc_mv;

		npc = ( PTFrame_GetPlayerNum(work) <= i );
		b_parent = (PTFrameGetCurrentID(work) == COMM_PARENT_ID);
		npc_mv = ( b_parent && npc );	//自分が親で、デモ要求がＮＰＣ

		pCommPoke = &parent->Team[i];
		pPokeDraw = &wk->drawPoke[i];

		GF_ASSERT( pCommPoke );
		GF_ASSERT( pPokeDraw );

		PokeID = pCommPoke->Active;
		pPokeDraw->PokeID = PokeID;
		ActiveID = i * ENTRY_MAX + pCommPoke->Active;

		poke_act = PokeAct_GetPokeActPtr( ptr->PokeActCntPtr, ActiveID );

		pPokeDraw->PokeAct = poke_act;

		GF_ASSERT( poke_act );

		// 上画面ポケモンのスタミナ状態(自分のみ)
		if( i == PTFrameGetCurrentID(work) )
		{
			u8 baseIdx;
			u8 pkgr_1;
			u8 pkgr_2;
			BOOL bSweat_1;
			BOOL bSweat_2;

			baseIdx = pCommPoke->Active;

			pkgr_1 = ( (baseIdx+2) % ENTRY_MAX ) + i * ENTRY_MAX;
			pkgr_2 = ( (baseIdx+1) % ENTRY_MAX ) + i * ENTRY_MAX;

			bSweat_1 = ( (parent->StateBit>>(pkgr_1*2)) & 0x3 ) == POKE_STA_ST_SWEAT;
			bSweat_2 = ( (parent->StateBit>>(pkgr_2*2)) & 0x3 ) == POKE_STA_ST_SWEAT;

			DropSub_DispStamina( wk->cntSub, bSweat_1, bSweat_2 );
		}

		// 当てられた側のヒットエフェクト(初回のみ表示)
		if( pPokeDraw->HitTypePre != HIT_EFF_TYPE_NULL && pCommPoke->HitType != HIT_EFF_TYPE_NULL )
		{
			POS_DATA PosA, PosB, PosDiff, PosRes;
			int HitNetID = pCommPoke->HitNetID;
			BOOL bDrawHitEff = 0;	///< ヒットエフェクト表示判定フラグ

			if( HitNetID <= i )
			{
				HitNetID--;
			}

			// タックル衝突
			if( pCommPoke->HitType == HIT_EFF_TYPE_TACKLE )
			{
				// 自分がタックル
				if( PTFrameGetCurrentID(work) == HitNetID )
				{
					StartShakeTask( &ptr->wkShake, 4, 10 );
				}

				bDrawHitEff = 1;
				Snd_SePlay( SEQ_SE_GS_TACKLEHIT );
			}
			// プレス衝突
			else if( pCommPoke->HitType == HIT_EFF_TYPE_PLESS )
			{
				// 自分がプレス
				if( PTFrameGetCurrentID(work) == HitNetID )
				{
					StartShakeTask( &ptr->wkShake, 10, 15 );
				}

				bDrawHitEff = 1;
				Snd_SePlay( SEQ_SE_GS_PRESSHIT );
			}

			// ヒットエフェクト表示
			if( bDrawHitEff )
			{
				// ２点の中心を算出
				PosA.X = pCommPoke->PokeX;
				PosA.Y = pCommPoke->PokeY;
				PosB.X = parent->Team[HitNetID].PokeX;
				PosB.Y = parent->Team[HitNetID].PokeY;

				PosDiff.X = MATH_IAbs( PosA.X - PosB.X ) / 2;
				PosDiff.Y = MATH_IAbs( PosA.Y - PosB.Y ) / 2;

				PosRes.X = MATH_IMin( PosA.X, PosB.X ) + PosDiff.X;
				PosRes.Y = MATH_IMin( PosA.Y, PosB.Y ) + PosDiff.Y;

				// ヒットエフェクト表示
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_HIT_EFF ], PosRes.X, PosRes.Y, OBJ_DISTANCE );
				CATS_ObjectAnimeReStartCap( pPokeDraw->cap[ PCAP_HIT_EFF ] );
				CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_HIT_EFF ], CATS_ENABLE_TRUE );

				HOSAKA_PRINT("view hitEff:[%d]&[%d] \n", i, HitNetID );
			}
		}
		pPokeDraw->HitTypePre = pCommPoke->HitType;
		pPokeDraw->HitNetIDPre = pCommPoke->HitNetID;

		// 方向
		PokeAct_SetDir( poke_act, pCommPoke->Dir+1 );

		// ====================================================================================
		// 復活
		// ====================================================================================
		// 開始
		if( pCommPoke->State == POKE_ST_REVIVE && pPokeDraw->StatePre != POKE_ST_REVIVE )
		{
			// グレースケールパレット転送
			PokeAct_ChangePalette( ptr->PokeActCntPtr, ActiveID, NNS_G2D_VRAM_TYPE_2DMAIN, pPokeDraw->PalData[PokeID].MonsPal[1] );
		}
		// 終了
		else if( pPokeDraw->StatePre == POKE_ST_REVIVE && pCommPoke->State != POKE_ST_REVIVE )
		{
			// 通常パレット転送
			PokeAct_ChangePalette( ptr->PokeActCntPtr, ActiveID, NNS_G2D_VRAM_TYPE_2DMAIN, pPokeDraw->PalData[PokeID].MonsPal[0] );
		}

		// ====================================================================================
		// タックル予備動作、タックル、プレス開始
		// ====================================================================================
		// タックル予備動作開始
		if( pCommPoke->State == POKE_ST_TACKLE_IN && pPokeDraw->StatePre != POKE_ST_TACKLE_IN )
		{
			HOSAKA_PRINT("view [%d] Snd Tackle in \n",i);
			// 自分
			if( PTFrameGetCurrentID(work) == i )
			{
				Snd_SePitchPlay( SEQ_SE_GS_TACKLEHATUDOU, 256 ); // 2音高く 半音=64
			}
			else
			{
				Snd_SePlayVol( SEQ_SE_GS_TACKLEHATUDOU2, 64 );
			}
		}
		// プレス開始
		else if( pCommPoke->State == POKE_ST_PRESS_END && pPokeDraw->StatePre != POKE_ST_PRESS_END )
		{
			DROP_POKE_PARAM* p_prm = GetDropPokeParam( work, i, PokeID );

			// ジャンプレベルが0以上のときのみ鳴る(ディグダ処理)
			if( p_prm->JumpLevel > 0 )
			{
				// 着地音
				Snd_SePlay( SEQ_SE_GS_PRESSCHAKUCHI );
			}
		}

		// タックル開始
		if( ( pCommPoke->State == POKE_ST_TACKLE && pPokeDraw->StatePre != POKE_ST_TACKLE ) )
		{
			u16 anm_seq = NANR_drop_obj_main_tackle_U + pCommPoke->Dir;
			// タックルエフェクト開始
			CATS_ObjectAnimeSeqCheckSetCap( pPokeDraw->cap[ PCAP_TACKLE ], anm_seq );
			CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_TACKLE ], CATS_ENABLE_TRUE );

			HOSAKA_PRINT("view [%d] Tackle Start Effect 開始!\n", i );
		}
		// タックル停止開始
		else
		if ( ( pCommPoke->State != POKE_ST_TACKLE && pPokeDraw->StatePre == POKE_ST_TACKLE ) ||
			 ( pCommPoke->State == POKE_ST_TACKLE_END && pPokeDraw->StatePre == POKE_ST_TACKLE_IN ) )	// 誰かに当てた
		{
			u16 anm_seq = NANR_drop_obj_main_tackle_U2 + pCommPoke->Dir;

			// タックル終了エフェクト開始
			CATS_ObjectAnimeSeqSetCap( pPokeDraw->cap[ PCAP_TACKLE ], anm_seq );
			CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_TACKLE ], CATS_ENABLE_TRUE );

			HOSAKA_PRINT("view [%d] Tackle End Effect 開始！ HitNetID:%d \n", i, pCommPoke->HitNetID );
		}


		// ====================================================================================
		// タックルエフェクト
		// ====================================================================================
		// アニメ再生中は
		if( CATS_ObjectAnimeActiveCheckCap( pPokeDraw->cap[ PCAP_TACKLE ] ) == TRUE )
		{
			// 座標更新
			CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_TACKLE ], pCommPoke->PokeX, pCommPoke->PokeY,  OBJ_DISTANCE );
		}

		// タックル中は
		if( pCommPoke->State == POKE_ST_TACKLE )
		{
			// アニメ停止
			PokeAct_SetAnm( poke_act, ANM_TYPE_STOP );
		}
		// ====================================================================================

		// 画面外例外処理
		if( pCommPoke->PokeX == 0 || pCommPoke->PokeX == 255 ||
			pCommPoke->PokeY == 0 || pCommPoke->PokeY == 255 )
		{
			// ポケモン非表示
			PokeAct_DispCnt( poke_act, FALSE );
			CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_TACKLE ], CATS_ENABLE_FALSE );
			CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_STA_EFF ], CATS_ENABLE_FALSE );
		}
		else
		// 疲労エフェクト
		{
			BOOL bChange = ( pCommPoke->ChgState != CHG_BALL_NONE );
			BOOL bFall = (pCommPoke->State == POKE_ST_FALL);
			u8 StaState = (parent->StateBit>>(ActiveID*2)) & 0x3; // 2ビットマスク
			s16 PosX = pCommPoke->PokeX;
			s16 PosY = pCommPoke->PokeY - pCommPoke->PokeZ - PANIC_DIFF_Y;

			CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_STA_EFF ], PosX, PosY, OBJ_DISTANCE );

			// 落下、交代中は表示しない
			if( bFall || bChange )
			{
				StaState = POKE_STA_ST_NORMAL;
			}
			// 疲労 ピヨピヨ
			else if( StaState == POKE_STA_ST_PANIC )
			{
				CATS_ObjectAnimeSeqCheckSetCap( pPokeDraw->cap[ PCAP_STA_EFF ], NANR_drop_obj_main_panic );
				if( Snd_SePlayCheck(SE_THLON_RUNNING_OUT) == FALSE ){	//再生終了していたら
					Snd_SePlay( SE_THLON_RUNNING_OUT );		//疲労音
				}
			}
			else if( StaState == POKE_STA_ST_SWEAT )
			{
				CATS_ObjectAnimeSeqCheckSetCap( pPokeDraw->cap[ PCAP_STA_EFF ], NANR_drop_obj_main_sweat );
			}

			// ノーマル以外のときのみ表示
			CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_STA_EFF ], StaState != POKE_STA_ST_NORMAL );
		}

		// 交代デモ
		ChangeDemo( pCommPoke, pPokeDraw, wk->cntSub, work, i );

		// 落下／復活 検出
		{
			u8 bFall = (pCommPoke->State == POKE_ST_FALL);

			// 落下開始
			if( bFall == 1 && pPokeDraw->bFallBitPre == 0 )
			{
				pPokeDraw->bFalling = 1;
				pPokeDraw->FallFrame = COMM_FALL_SYNC;
				pPokeDraw->fxScale = FX32_CONST(1);

				// 落下音
				Snd_SePlay( SE_CATCH_FALL );

				// 上画面ポケアイコンをボールに
				DropSub_SetPokeIconOrBall( wk->cntSub, i, PokeID, TRUE );

				// 落下したのが自分、若しくは親でNPCだった場合は上画面交代演出リクエスト
				if( i == PTFrameGetCurrentID(work) || npc_mv )
				{
					pPokeDraw->bFallChangeSub = 1;
				}

			}
			// 落下復活
			else if( bFall == 0 && pPokeDraw->bFallBitPre == 1 )
			{
				s16 px, py;

				px = pCommPoke->PokeX;
				py = pCommPoke->PokeY;

				// 煙アニメ開始
				CATS_ObjectPosSetCap_SubSurface( pPokeDraw->cap[ PCAP_SMOKE ], px, py, OBJ_DISTANCE );
				CATS_ObjectEnableCap( pPokeDraw->cap[ PCAP_SMOKE ], CATS_ENABLE_TRUE );
				CATS_ObjectAnimeReStartCap( pPokeDraw->cap[ PCAP_SMOKE ] );

				// 煙再生SE
				PlaySe( SE_RUN_CHANGE_IN2, ( i == PTFrameGetCurrentID(work) ) );

				// 上画面ポケアイコン操作
				DropSub_SetPokeIconOrBall( wk->cntSub, i, PokeID, FALSE );

				// ポケモン表示
				PokeAct_DispCnt( poke_act, TRUE );

				pPokeDraw->bFalling = 0;

				HOSAKA_PRINT("view [%d,%d] Poke Revive start !\n", i/ENTRY_MAX, i%ENTRY_MAX );
			}

			// 前回のビット状態を保持
			pPokeDraw->bFallBitPre = bFall;
		}

		// 移動(影を含む）
		PokeAct_SetPosTouch( poke_act, pCommPoke->PokeX, pCommPoke->PokeY, TRUE);
		// 移動(ジャンプ高度)
		PokeAct_SetPosTouch( poke_act, pCommPoke->PokeX, pCommPoke->PokeY - pCommPoke->PokeZ, FALSE);

		// 落下処理
		if( pPokeDraw->bFalling )
		{
			// 上画面変更処理
			if( pPokeDraw->bFallChangeSub )
			{
				if( npc_mv )
				{
					if( DropSub_ChangePokeNpc( ptr->cntSub, i, pPokeDraw->PokeID ) )
					{
						pPokeDraw->bFallChangeSub = 0;
					}
				}
				else
				{
					if( DropSub_ChangePokeMain( ptr->cntSub, i, pPokeDraw->PokeID ) )
					{
						pPokeDraw->bFallChangeSub = 0;
					}
				}
			}

			// 落下処理
			if( --pPokeDraw->FallFrame <= 0 )
			{
				PokeAct_DispCnt( poke_act, FALSE );
			}
			else
			{
				f32 Scale;
				int poke_x = pCommPoke->PokeX;
				int poke_y = pCommPoke->PokeY;

				pPokeDraw->fxScale -= FX32_CONST(0.1);

				Scale = FX_FX32_TO_F32(pPokeDraw->fxScale);

				PokeAct_SetScale( poke_act, Scale, Scale );

				// タッチ座標から影座標を算出
				PokeAct_GetShadowPosByTouchPos( poke_act, poke_x, poke_y, &poke_x, &poke_y );

				// 子機処理：下への落下
				if( poke_y >= RING_Y_MAX )
				{
					int val = COMM_FALL_SYNC - pPokeDraw->FallFrame;

					val = val * 4 + pCommPoke->PokeY;
					PokeAct_SetPosTouch( poke_act, pCommPoke->PokeX, val, TRUE );
				}
			}
		}
		else
		// Y座標から拡縮
		{
			f32 scale_x = RING_SCALE_MAX;
			f32 scale_y = RING_SCALE_MAX;
			scale_x -= FX_FX32_TO_F32( FX32_CONST( RING_SCALE_VAL ) * ( RING_Y_MAX - pCommPoke->PokeY ) / RING_SCALE_DOT );
			scale_y = scale_x;

			// 予備動作:スケール上書き処理
			if( pCommPoke->State == POKE_ST_TACKLE_IN )
			{
				scale_x *=  0.8 - ( 0.1 * pPokeDraw->PreActCnt );
				HOSAKA_PRINT("view [%d] タックル 予備動作 cnt:%d scale:%f pos:[%d,%d] \n", i, pPokeDraw->PreActCnt, scale_x, pCommPoke->PokeX, pCommPoke->PokeY );

				if( pPokeDraw->PreActCnt < TACKLE_PRE_SYNC )
				{
					pPokeDraw->PreActCnt++;
				}
			}

			if( pCommPoke->State == POKE_ST_JUMP_IN )
			{
				DROP_POKE_PARAM* p_prm = GetDropPokeParam( work, i, PokeID );

				// 最初のみ音を鳴らす
				if( pPokeDraw->PreActCnt == 0 )
				{
					// ジャンプレベルが0ならば飛べない(ディグダ処理)
					if( p_prm->JumpLevel == 0 )
					{
						if( i == PTFrameGetCurrentID(work) )
						{
							Snd_SePlay( SE_PKTH_JUMP_FAILED );
						}
					}
					else
					{
						Snd_SePlay( SE_PKTH_JUMP );
					}
				}

				scale_y *=  0.8 - ( 0.1 * pPokeDraw->PreActCnt );
				HOSAKA_PRINT("view [%d] ジャンプ 予備動作 cnt:%d scale:%f \n", i, pPokeDraw->PreActCnt, scale_y );

				if( pPokeDraw->PreActCnt < JUMP_IN_SYNC )
				{
					pPokeDraw->PreActCnt++;
				}
			}

			if( pCommPoke->State != POKE_ST_JUMP_IN && pCommPoke->State != POKE_ST_TACKLE_IN )
			{
				// 予備動作後なら初期化処理
				if( pPokeDraw->PreActCnt > 0 )
				{
					Poke_SetAffineVec( poke_act, TRUE );
					pPokeDraw->PreActCnt = 0;
				}
			}
			else
			{
				// 一時的にアフィン中心座標を足元に
				Poke_SetAffineVec( poke_act, FALSE );
			}

			PokeAct_SetScale( poke_act, scale_x, scale_y );
		}

		// 前回の状態を保持
		pPokeDraw->StatePre = pCommPoke->State;
	}

	// Y軸ソートのための情報を作成
	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		DROP_COMM_TEAM* pCommPoke;
		POKE_ACT_PTR poke_act;
		int ActiveID;
		int poke_x;
		int poke_y;

		pCommPoke = &parent->Team[i];
		ActiveID = i * ENTRY_MAX + pCommPoke->Active;
		poke_act = PokeAct_GetPokeActPtr( ptr->PokeActCntPtr, ActiveID );

		poke_x = pCommPoke->PokeX;
		poke_y = pCommPoke->PokeY;

		// タッチ座標から影座標を算出
		PokeAct_GetShadowPosByTouchPos( poke_act, poke_x, poke_y, &poke_x, &poke_y );

		sort[i].PosY = poke_y;
		sort[i].act = poke_act;
	}

	// Y軸ソート
	MATH_QSort( (void*)sort, ENTRY_MEMBER_NUM_MAX, sizeof(POKE_PRI_SORT), PokePriSort, NULL );
	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		PokeAct_SetPriority( sort[i].act, PRI_POKE + i );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信ビューア 生成
 *
 *	@param	VIEWER_WORK* wk
 *	@param	cntSub
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VIEWER_Init( VIEWER_WORK* wk, CATS_SYS_PTR csp, CATS_RES_PTR crp, DROP_SUB_CNT_PTR cntSub, POKE_ACT_CNT_PTR cntPokeAct, HEAPID HeapID )
{
	int i;
	wk->cntSub = cntSub;
	wk->csp = csp;
	wk->crp = crp;

	wk->capUI[ CAPUI_CANGE ] = OBJ_AddCapMain( csp, crp, CAP_CHG_X, CAP_CHG_Y, NANR_drop_obj_main_chg_n, PRI_UI );

	// チーム毎のCAP生成
	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		int j;
		POKE_DRAW* pDrawPoke = &wk->drawPoke[i];
		u8 ActiveID = i * ENTRY_MAX;

		// ポインタを保持
		pDrawPoke->PokeAct = PokeAct_GetPokeActPtr( cntPokeAct, ActiveID );

		// パレット生成
		for( j=0; j<ENTRY_MAX; j++ )
		{
			POKE_ACT_PTR poke_act = PokeAct_GetPokeActPtr( cntPokeAct, ActiveID + j );
			PCLACT_PTR pcl_act = PokeAct_GetPclActPtr( poke_act );
			const MONS_INFO* info = PCLACT_GetMonsInfo( pcl_act );

			PCLACT_GetPalData(info, HeapID, (u8*)&pDrawPoke->PalData[j].MonsPal[0][0]);
			MI_CpuCopy8(pDrawPoke->PalData[j].MonsPal[0], pDrawPoke->PalData[j].MonsPal[1], 16*2);

			// グレースケールパレット
			PaletteGrayScale(pDrawPoke->PalData[j].MonsPal[1], 16 );
		}

		// ヒットエフェクト
		pDrawPoke->cap[ PCAP_HIT_EFF ] = OBJ_AddCapMain( csp, crp, 0, 0, NANR_drop_obj_main_hit, PRI_EFF_HIT );
		CATS_ObjectEnableCap( pDrawPoke->cap[ PCAP_HIT_EFF ], CATS_ENABLE_FALSE );

		// 疲労CAP
		pDrawPoke->cap[ PCAP_STA_EFF ] = OBJ_AddCapMain( csp, crp, 0, 0, NANR_drop_obj_main_panic, PRI_EFF_STAMINA );
		CATS_ObjectEnableCap( pDrawPoke->cap[ PCAP_STA_EFF ], CATS_ENABLE_FALSE );

		// 体当たり用CAP
		pDrawPoke->cap[ PCAP_TACKLE ] = OBJ_AddCapMain( csp, crp, 0, 0, NANR_drop_obj_main_tackle_U, PRI_EFF_TACKLE );
		CATS_ObjectEnableCap( pDrawPoke->cap[ PCAP_TACKLE ], CATS_ENABLE_FALSE );

		// ボールCAP
		pDrawPoke->cap[ PCAP_BALL ] = OBJ_AddCapMain( csp, crp, 0, 0, NANR_drop_obj_main_ball, PRI_BALL );
		CATS_ObjectEnableCap( pDrawPoke->cap[ PCAP_BALL ], CATS_ENABLE_FALSE );

		// 煙CAP
		pDrawPoke->cap[ PCAP_SMOKE ] = OBJ_AddCapMain( csp, crp, 0, 0, NANR_drop_obj_main_smoke, PRI_EFF_SMOKE );
		CATS_ObjectEnableCap( pDrawPoke->cap[ PCAP_SMOKE ], CATS_ENABLE_FALSE );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信ビューア 描画
 *
 *	@param	VIEWER_WORK* cnt
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VIEWER_Draw( VIEWER_WORK* wk, PKTHLN_PTR work )
{
	int i;
	COMM_EVENT_COMMON_DATA* data;
	DROP_COMM_PARENT_PTR parent;
	PKTHLN_DROP_PTR ptr;

	data = PTFrame_GetEvtComCmnData(work);
	parent = PKTHLN_CommPack_GetData(&data->ReciveShareData);
	ptr = PTFrame_GetEventWorkPtr(work);

#ifdef PM_DEBUG
	// データ漏れ検出
	GF_ASSERT_MSG( parent->padding[0] == 0, "recive padding[0]=%d",parent->padding[0] );
	GF_ASSERT_MSG( parent->padding[1] == 0, "recive padding[1]=%d",parent->padding[1] );
	GF_ASSERT_MSG( parent->padding[2] == 0, "recive padding[2]=%d",parent->padding[2] );
	GF_ASSERT_MSG( parent->padding[3] == 0, "recive padding[3]=%d",parent->padding[3] );
	GF_ASSERT_MSG( parent->DuumyBit1 == 0, "recive DuumyBit1=%d", parent->DuumyBit1 );
	GF_ASSERT_MSG( parent->DuumyBit2 == 0, "recive DummyBit2=%d", parent->DuumyBit2 );
#endif

	// ポケモン関係の描画
	VIEWER_POKE_Draw( wk, parent, work );

	// ラスト3秒前表示
	PTFrame_DispLastCount( work, parent->GameTimer );

	// カンカンカーン
	if( parent->GameTimer == SE_LAST_SYNC )
	{
		Snd_SePlay( SE_THLON_LAST );
	}

	// 交代ボタン
	{
		u8 AnimeSeq;
		u8 ButtonState;

		ButtonState = parent->Team[ PTFrameGetCurrentID(work) ].BtnSt;

		switch( ButtonState )
		{
			case BTN_ST_PUSH_OK			: AnimeSeq = NANR_drop_obj_main_chg;		break;
			case BTN_ST_PUSH_NG			: AnimeSeq = NANR_drop_obj_main_chg_n;		break;
			case BTN_ST_PUSH_OK_PUSH	: AnimeSeq = NANR_drop_obj_main_chg_p;		break;
			case BTN_ST_PUSH_NG_PUSH	: AnimeSeq = NANR_drop_obj_main_chg_np;		break;
			case BTN_ST_PUSH_OK_FLASH	: AnimeSeq = NANR_drop_obj_main_chg_urge;	break;
			default : GF_ASSERT(0);
		}

		CATS_ObjectAnimeSeqCheckSetCap( wk->capUI[ CAPUI_CANGE ], AnimeSeq );

		// アニメが変更された時のみ
		if( ButtonState != wk->ChangeButtonStatePre )
		{
			if( ButtonState == BTN_ST_PUSH_OK_PUSH )
			{
				Snd_SePlay( SEQ_SE_GS_CHANGE1 );
			}
			else if( ButtonState == BTN_ST_PUSH_NG_PUSH )
			{
				Snd_SePlay( SEQ_SE_GS_CHANGE2 );
			}
		}

		wk->ChangeButtonStatePre = ButtonState;
	}

	{
		 RANK_DATA Rank[ ENTRY_MEMBER_NUM_MAX ];

		// ランクデータ生成
		for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
		{
			Rank[i].NetID = i;
			Rank[i].Score = parent->Team[i].Score;
		}

		// ソート
		MATH_QSort( (void*)Rank, ENTRY_MEMBER_NUM_MAX, sizeof(RANK_DATA), RankSort, NULL );

		for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
		{
			// 点数更新を検知
			if( parent->Team[i].Score != wk->ScorePre[i] )
			{
				DropSub_AddScore( wk->cntSub, i, parent->Team[i].Score, Rank );
				HOSAKA_PRINT("[%d]Score=%d \n", i, parent->Team[i].Score );
			}
			// 前回のスコアを保持
			wk->ScorePre[i] = parent->Team[i].Score;
		}
	}

	// 表示時間更新
	DropSub_DispTime( ptr->cntSub, parent->GameTimer/GAME_FPS );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信ビューア削除
 *
 *	@param	VIEWER_WORK* wk
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VIEWER_Exit( VIEWER_WORK* wk )
{
	int i;
	int tc;	///< team counter
	int cc;	///< cap counter

	GF_ASSERT( wk->csp );
	GF_ASSERT( wk->crp );

	for( i=0; i<CAPUI_MAX; i++ )
	{
		GF_ASSERT( wk->capUI[i] );
		CATS_ActorPointerDelete_S( wk->capUI[i] );
	}

	for( tc=0; tc<ENTRY_MEMBER_NUM_MAX; tc++ )
	{
		POKE_DRAW* pDrawPoke = &wk->drawPoke[tc];

		for( cc=0; cc<PCAP_MAX; cc++ )
		{
			CATS_ActorPointerDelete_S( pDrawPoke->cap[cc] );
		}
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信ビューア：全てのオブジェを停止
 *
 *	@param	VIEWER_WORK* wk
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void VIEWER_AllObjStop( VIEWER_WORK* wk )
{
	int i, j;

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		for( j=0; j<PCAP_MAX; j++ )
		{
			CATS_ObjectAutoAnimeSetCap( wk->drawPoke[i].cap[j], FALSE );
		}
	}

	// CAP UI
	for( i=0; i<CAPUI_MAX; i++ )
	{
		CATS_ObjectAutoAnimeSetCap( wk->capUI[ i ], FALSE );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	Aがふっとぶか？
 *
 *	@param	POKE_STATE StateA
 *	@param	StateB
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL CheckPokeHitEnable( POKE_STATE StateA, POKE_STATE StateB )
{
	// 自分がプレスなら絶対に当たらない
	if( StateA == POKE_ST_PRESS ){ return FALSE; }

	// 相手がプレスなら絶対に当たる
	if( StateB == POKE_ST_PRESS ){ return TRUE; }

	// 自分がタックルの時、相手がタックル,プレス以外なら当たらない
//	if( StateA == POKE_ST_TACKLE && (StateB != POKE_ST_TACKLE || StateB != POKE_ST_PRESS ) ){ return FALSE; }

	// 双方タックルなら当たる
//	if( StateA == POKE_ST_TACKLE && StateB == POKE_ST_TACKLE ){ return TRUE; }


	// 後はみんなふっとぶ
	return TRUE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ヒット中の向き変更処理
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Hit_DirChange( RING_POKE* pRPoke, VecFx32* pIntersect )
{
	if( pRPoke->State != POKE_ST_TACKLE && pRPoke->State != POKE_ST_PRESS )
	{
		pRPoke->Dir = Poke_CalcDir( pIntersect );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	相手をヒットウェイトにする状態か判定
 *
 *	@param	RING_POKE* pAtkPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Check_StateToHitWait( POKE_STATE state )
{
	return( state == POKE_ST_TACKLE || state == POKE_ST_PRESS );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン同士のヒット演算
 *
 *	@param	pPokeA		自分のリングポケ
 *	@param	pPokeB		相手のリングポケ
 *	@param	pIntersect	衝突した方向(正規化済み)
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL CalcPokeHit( RING_POKE* pPokeA, RING_POKE* pPokeB, VecFx32* pIntersect )
{
	fx32 fxPush = pPokeB->DropPoke->cpp->fxPush;
	fx32 fxPower = pPokeB->DropPoke->cpp->fxPower;

	// 自分がふっとぶ処理
	if( CheckPokeHitEnable( pPokeA->State, pPokeB->State ) )
	{
		// 相手がタックルなら弾き値、威力増加
		if( pPokeB->State == POKE_ST_TACKLE )
		{
			fxPush = fxPush * TACKLE_PUSH_MUL;
			fxPower = fxPower * TACKLE_POWER_MUL;
			pPokeB->Score = MATH_IMin( pPokeB->Score + SCORE_TACKLE, DROP_SCORE_MAX );
			// AP:ポイント獲得回数
			PTFrame_AddActionPoint( pPokeB->work, pPokeB->NetID, pPokeB->PokeID, ADD_TYPE_GET, 1 );
			// AP:アタック回数
			PTFrame_AddActionPoint( pPokeB->work, pPokeB->NetID, pPokeB->PokeID, ADD_TYPE_ATTACK, 1 );
		}
		// 相手がプレスなら弾き値、威力をプレス用に
		else if( pPokeB->State == POKE_ST_PRESS )
		{
			fxPush = pPokeB->DropPoke->cpp->fxPressPush;
			fxPower = pPokeB->DropPoke->cpp->fxPressPower;
			pPokeB->Score = MATH_IMin( pPokeB->Score + SCORE_PRESS, DROP_SCORE_MAX );
			// AP:ポイント獲得回数
			PTFrame_AddActionPoint( pPokeB->work, pPokeB->NetID, pPokeB->PokeID, ADD_TYPE_GET, 1 );
			// AP:アタック回数
			PTFrame_AddActionPoint( pPokeB->work, pPokeB->NetID, pPokeB->PokeID, ADD_TYPE_ATTACK, 1 );
		}
		else
		{
			// 相手がその他の状態なら固定値
			fxPush = PUSH_NORMAL_FX;
		}

		// 衝突した力を分解して足しこむ
		VEC_MultAdd( fxPush, pIntersect, &pPokeA->PushVec, &pPokeA->PushVec );

		// 速度規制
		VEC_Clamp( &pPokeA->PushVec, POKE_PUSH_VEC_MAX_FX );

		HOSAKA_PRINT("hitting! [%d,%d:st=%d:pow:%f]->[%d,%d:st=%d]:{%f, %f} \n",
				pPokeB->NetID, pPokeB->PokeID, pPokeB->State,
				FX_FX32_TO_F32(pPokeB->DropPoke->cpp->fxPush),
				pPokeA->NetID, pPokeA->PokeID, pPokeA->State,
				FX_FX32_TO_F32(pPokeA->PushVec.x), FX_FX32_TO_F32(pPokeA->PushVec.y) );

		// 衝突したポケモンの向きを変更
		Hit_DirChange( pPokeB, pIntersect );

		// タックルかプレスを受けた時のみ
		if( Check_StateToHitWait( pPokeB->State ) )
		{
			// お互いのポケをヒットストップ
			pPokeA->HitStopFrame = HIT_STOP_SYNC;
			pPokeB->HitStopFrame = HIT_STOP_SYNC;

			// 攻撃された側のスタミナを減らす
			Stamina_Dec( pPokeA, fxPower );
		}

		// 押され対象
		// 初回ヒット・もしくは相手がタックル・プレスのときのみ更新
		if( pPokeA->PushPoke == NULL || Check_StateToHitWait( pPokeB->State ) )
		{
			pPokeA->PushPoke = pPokeB;

			// 相手が既に自分以外の誰かに押されていたら対象を親に
			if( pPokeB->PushPoke != NULL )
			{
				if( pPokeB->PushPoke != pPokeA )
				{
					pPokeA->PushPoke = pPokeB->PushPoke;
				}
			}

			HOSAKA_PRINT("[%d,%d] PushPoke->NetID:%d \n", pPokeA->NetID, pPokeA->PokeID, pPokeA->NetID );
		}

		// 押された情報を保持
		pPokeA->bPush = 1;
		pPokeA->HitState = pPokeB->State;

		// 通信に渡す衝突タイプを設定
		if( Check_StateToHitWait(pPokeB->State) )
		{
			// プレスは上書きしない
			if( pPokeA->HitType != HIT_EFF_TYPE_PLESS )
			{
				switch( pPokeB->State )
				{
					case POKE_ST_TACKLE : pPokeA->HitType = HIT_EFF_TYPE_TACKLE; break;
					case POKE_ST_PRESS : pPokeA->HitType = HIT_EFF_TYPE_PLESS; break;
				}
			}

			// 通信に渡す衝突IDを保持(0は自分(NULL))
			{
				int idx = pPokeB->NetID;

				if( idx < pPokeA->NetID ){ idx++; }
				pPokeA->HitNetID = idx;
			}

			// カウンタセット
			pPokeA->HitSendCnt = HIT_SEND_SYNC;
		}
		else
		{
			pPokeA->bHitSndNormal = 1;
		}


		// 状態チェック
		GF_ASSERT( pPokeB->State != POKE_ST_FALL &&
				   pPokeB->State != POKE_ST_REVIVE );

		GF_ASSERT( pPokeA->State != POKE_ST_FALL &&
				   pPokeA->State != POKE_ST_REVIVE &&
				   pPokeA->State != POKE_ST_PRESS );

		return TRUE;
	}

	return FALSE;
}


//-----------------------------------------------------------------------------
/**
 *	@brief	ポケアクター 生成
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_Create( PKTHLN_DROP_PTR ptr, PKTHLN_PTR work )
{
	// ワーク生成
	ptr->PokeActCntPtr = PokeAct_Init(
			ptr->HeapID, ACT_POKE_MAX, PRI_POKE,
			ptr->CmnResCntPtr, CATS_GetClactSetPtr(ptr->crp) );

	// ポケアクター セットアップ
	{
		int i;
		MONS_INFO info[ ACT_POKE_MAX ];
		PCLACT_SET_PARAM param = {0};

		for( i=0; i<ACT_POKE_MAX; i++ )
		{
			const PKTHPOKE_PARAM* pp;

			PTFrame_SetMonsInfo( work, i/ENTRY_MAX, i%ENTRY_MAX, &info[i] );
			pp = PTFrame_GetPkthlnParam(work, i/ENTRY_MAX, i%ENTRY_MAX);
			param.ShadowPos[i] = PTFrame_GetPokeShdwPosType( pp );
		}

		param.BGPriority = BGPRI_POKE;
		param.ResVramType = NNS_G2D_VRAM_TYPE_2DMAIN;
		param.DrawArea = NNS_G2D_VRAM_TYPE_2DMAIN;

		PokeAct_SetPokeActor( ptr->PokeActCntPtr, ACT_POKE_MAX, info, &param, 0, 0 );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケアクター 初期化
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_Init( PKTHLN_DROP_PTR ptr, PKTHLN_PTR work )
{
	int i;
	EVT_HIT_DATA hit_data = {0};
	PEN_POS pen_pos;

	// 当たり判定データをロード
	ArchiveDataLoad(hit_data.Data, ARC_PKTHEVT_PARAM, NARC_pkthln_evt_info_drop_hit_bin);

	GF_ASSERT(ptr);
	GF_ASSERT( hit_data.Data );

	// コンバートデータを取得
	SetDropPokeParam( work );

	//ポケセルアクター関連初期化
	for (i=0;i<ACT_POKE_MAX;i++)
	{
		POKE_ACT_PTR poke_ptr;
		u8 NetID = i / ENTRY_MAX;
		u8 PokeID = i % ENTRY_MAX;
		DROP_POKE* pDropPoke = &ptr->wkServer.TeamData[ NetID ].DropPoke[ PokeID ];

		poke_ptr = PokeAct_GetPokeActPtr(ptr->PokeActCntPtr, i);

		PokeAct_DispCnt(poke_ptr, (PokeID==0) );	// チーム毎にアクティブポケを表示(初回は絶対0)

		//あたり判定セット
		{
			HIT_TYPE hit_type;
			hit_type = PTFrame_GetPokeHitType( PTFrame_GetPkthlnParam(work, NetID, PokeID ) );
			PokeAct_SetHitData(poke_ptr, hit_data.Data[hit_type-1].Ofs, hit_data.Data[hit_type-1].Range);
		}

		//ポケモン初期位置
		{
			POKE_ACT_PTR poke_ptr = PokeAct_GetPokeActPtr(ptr->PokeActCntPtr, i);
			PCLACT_PTR	pclact_ptr = PokeAct_GetPclActPtr(poke_ptr);

			//アフィンセット
			PokeAct_SetAffine(poke_ptr, CLACT_AFFINE_DOUBLE);
			// アフィン中心座標をポケモンの中心に
			Poke_SetAffineVec( poke_ptr, TRUE );

			//テーブルで位置決定
			{
				int sx,sy;
				int tx,ty;
				int range;

				//影の位置
				sx = g_PokeInitPos[NetID].X;
				sy = g_PokeInitPos[NetID].Y;

				// 自分のポケモンの位置にペンの座標設定
				if( NetID == PTFrameGetCurrentID(work) )
				{
					pen_pos.X = sx;
					pen_pos.Y = sy;
				}

				//向きセット
				PokeAct_SetDir(poke_ptr, POKE_DIR_DOWN);

				//プライオリティセット
				PokeAct_SetPriority(poke_ptr, PRI_POKE);

				//アクター位置セット
				PokeAct_SetPosShadow(poke_ptr, sx, sy);

				//タッチ半径セット
				range = PTFrame_GetPokeTouchRange();
				PokeAct_SetTouchRange(poke_ptr, range);

				//タッチ中心座標取得
				PokeAct_GetTouchPosByShadowPos(poke_ptr, sx, sy, &tx, &ty);

				//データ保持
				pDropPoke->ID = i;
				pDropPoke->PokeActPtr = poke_ptr;
				pDropPoke->cpp = GetDropPokeParam( work, NetID, PokeID );
				pDropPoke->fxStamina = pDropPoke->cpp->fxStaminaMax;
			}
		} //ポケモン初期位置
	}	//end for

	// ペン表示
	PTFrame_SetPenByClSet( work, 0, ptr->CmnResCntPtr, CATS_GetClactSetPtr(ptr->crp), NNS_G2D_VRAM_TYPE_2DMAIN, 1, &pen_pos );

	// チームデータの初期化
	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		int sx, sy;
		int tx,ty;
		POKE_ACT_PTR poke_ptr;
		RING_POKE* pRPoke = &ptr->wkServer.TeamData[ i ].RingPoke;

		// 参照
		pRPoke->TeamData = &ptr->wkServer.TeamData[ i ];
		pRPoke->DropPoke = &ptr->wkServer.TeamData[ i ].DropPoke[ 0 ];
		pRPoke->cntSub = ptr->cntSub;
		pRPoke->work = work;

		sx = g_PokeInitPos[i].X;
		sy = g_PokeInitPos[i].Y;
		PokeAct_GetTouchPosByShadowPos( pRPoke->DropPoke->PokeActPtr, sx, sy, &tx, &ty );

		Poke_SetState( pRPoke, POKE_ST_ALIVE );
		pRPoke->Dir = POKE_DIR_DOWN;
		pRPoke->NowPos.x = tx * FX32_ONE;
		pRPoke->NowPos.y = ty * FX32_ONE;
		pRPoke->LastPos.x = tx * FX32_ONE;
		pRPoke->LastPos.y = ty * FX32_ONE;
		pRPoke->NetID = i;
		pRPoke->fxJumpG = JUMP_DEF_ACCEL_FX;
		pRPoke->BtnSt = BTN_ST_PUSH_NG;

		ptr->wkServer.TeamData[i].NetID = i;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケアクター 開放
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_Delete( PKTHLN_DROP_PTR ptr )
{
	PokeAct_Free( ptr->PokeActCntPtr );
}

//--------------------------------------------------------------
///	ヒット時の状態遷移用
//==============================================================
typedef enum {
	POKE_STATE_CHG_NONE = 0,		///< 継続
	POKE_STATE_CHG_TO_ALIVE,		///< 通常状態へ
	POKE_STATE_CHG_TO_TACKLE_END,	///< タックル終了（その後通常状態へ）
	POKE_STATE_CHG_TO_HIT_WAIT,		///< 抵抗無効時間へ
	POKE_STATE_CHG_MAX,
} POKE_STATE_CHG;

//-----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	RING_POKE* pRPoke
 *	@param	chg
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void SetStateByHitting( RING_POKE* pRPoke, POKE_STATE_CHG chg )
{
	switch( chg )
	{
		case POKE_STATE_CHG_NONE :
			break;

		case POKE_STATE_CHG_TO_ALIVE :
			Poke_SetState( pRPoke, POKE_ST_ALIVE );
			break;

		case POKE_STATE_CHG_TO_TACKLE_END :
//			Tackle_End( pRPoke );
			Poke_SetState( pRPoke, POKE_ST_TACKLE_END );
			pRPoke->TackleFrame = 1; // キャンセルを発生させるためにフレームを1にする
			break;

		case POKE_STATE_CHG_TO_HIT_WAIT :
			HitWait_Start( pRPoke );
			break;

		default : GF_ASSERT( 0 );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ヒット後に遷移する状態を取得
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static POKE_STATE_CHG GetStateByHitting( RING_POKE* pRPoke )
{
	POKE_STATE mine = pRPoke->State;
	POKE_STATE tar = pRPoke->HitState;
	POKE_STATE_CHG chg = POKE_STATE_CHG_NONE;

	// 双方プレスはここに来ない
	GF_ASSERT_MSG( (mine == tar) != POKE_ST_PRESS, "hit pless & pless" );

	// 押されていなければ、なにもしない
	if( pRPoke->bPush == 0 ){ return chg; }

	switch( mine )
	{
		// プレス
		case POKE_ST_PRESS :
			// 状態維持
			chg = POKE_STATE_CHG_NONE;
			break;

		case POKE_ST_TACKLE :
		case POKE_ST_TACKLE_IN :
		case POKE_ST_TACKLE_END :
		case POKE_ST_JUMP_IN :
		case POKE_ST_PRESS_END :
			if( tar == POKE_ST_PRESS || tar == POKE_ST_TACKLE )
			{
				// 抵抗無抵抗時間へ
				chg = POKE_STATE_CHG_TO_HIT_WAIT;
			}
			else
			{
				if( mine == POKE_ST_TACKLE )
				{
					// タックル終了へ
					chg = POKE_STATE_CHG_TO_TACKLE_END;
				}
				else
				{
					// 通常状態へ
					chg = POKE_STATE_CHG_TO_ALIVE;
				}
			}
			break;

		case POKE_ST_ALIVE :
		case POKE_ST_HIT_WAIT:
		case POKE_ST_JUMP :
			if( tar == POKE_ST_PRESS || tar == POKE_ST_TACKLE )
			{
				// 抵抗無抵抗時間へ
				chg = POKE_STATE_CHG_TO_HIT_WAIT;
			}
			else
			{
				// 状態維持
				chg = POKE_STATE_CHG_NONE;
			}
			break;

		case POKE_ST_NONE :
		case POKE_ST_REVIVE :
		case POKE_ST_FALL :
		case POKE_ST_JUMP_WAIT :
			// こないはず
			GF_ASSERT(0);
			break;

		default : GF_ASSERT (0);

	}

	return chg;
}


#ifdef DEBUG_DUMP_STATE
// 主人公の状態を記録(当たった瞬間にデバッグボタンを押していると表示)
static void _debug_dump_state( POKE_STATE debug_pre_state, POKE_STATE debug_pre_state2, POKE_STATE state1, POKE_STATE state2 )
{
	int i;
	static const int DUMP_MAX = 1000;
	static int dump_cnt = 0;
	static POKE_STATE state_dump[ DUMP_MAX ][4] = {0};

	// pre_me, pre_you, me, you
	state_dump[dump_cnt][0] = debug_pre_state;
	state_dump[dump_cnt][1] = debug_pre_state2;
	state_dump[dump_cnt][2] = state1;
	state_dump[dump_cnt][3] = state2;

	dump_cnt = MATH_IMin( dump_cnt+1, DUMP_MAX );

	if( sys.cont & PAD_BUTTON_DEBUG )
	{
		HOSAKA_PRINT("================================\n");

		for( i=0; i<dump_cnt; i++ )
		{
			HOSAKA_PRINT("%d pre_me:%d pre_you:%d me:%d you:%d \n", i,
				state_dump[i][0], state_dump[i][1], state_dump[i][2], state_dump[i][3] );
		}
	}
}
#endif

//====================================================
// @@@ タックル・プレスのヒット演出・ポイントがはいらない不具合の検証
//====================================================
// ・現在の作りだと1フレーム落ちるだけでエフェクトが欠落するのでもう少しどうにかしたい。

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの当たり判定を一斉計算
 *
 *	@param	PKTHLN_PTR work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_CalcPokeCollision( PKTHLN_PTR work )
{
	int i, j;
	PKTHLN_DROP_PTR ptr;
	u8 (*Invalid)[ENTRY_MEMBER_NUM_MAX]; ///< 二次元配列へのポインタ
	BOOL bHit = FALSE;

	ptr = PTFrame_GetEventWorkPtr(work);
	Invalid = ptr->wkServer.PokeInvalidCollision; ///< 処理フラグ

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		RING_POKE* pRPoke = &ptr->wkServer.TeamData[i].RingPoke;

		// 当たり判定無効状態だったら次のポケモンをチェック
		if( Poke_IsHitActive( pRPoke ) == FALSE || pRPoke->HitStopFrame > 0 )
		{
			continue;
		}

		for( j = i+1; j<ENTRY_MEMBER_NUM_MAX; j++ )
		{
			RING_POKE* pTar;

			pTar = &ptr->wkServer.TeamData[j].RingPoke;

#ifdef PM_DEBUG
			// ジャンププレスのときのみプリント
			if( pRPoke->State == POKE_ST_PRESS )
			{
				HOSAKA_PRINT("PRESS Invalid[%d][%d]=%d \n", i,j,Invalid[i][j] );
			}
#endif

			// 衝突判定
			if( Poke_IsHitActive( pTar ) && Poke_CheckCircle( pRPoke, pTar ) )
			{
				VecFx32 Intersect = {0};	///< 2点の中心点で定義される単位ベクトル
				BOOL bHitResult1 = FALSE;
				BOOL bHitResult2 = FALSE;

#ifdef DEBUG_DUMP_STATE
				POKE_STATE debug_pre_state = pRPoke->State;
				POKE_STATE debug_pre_state2 = pTar->State;
#endif

				// 押され状態でなくなったら、再度アタリチェック
				if( Invalid[i][j] && pRPoke->bPush == 0 )
				{
					Invalid[i][j] = Invalid[j][i] = 0;
					HOSAKA_PRINT("Invalid OFF [%d][%d]\n",i, j );
				}

				if( Invalid[i][j] ){ continue; }

				VEC_Subtract( &pRPoke->NowPos, &pTar->NowPos, &Intersect );

				// 0距離ヒットの場合はふっとび係数をランダム値
				if( Intersect.x == 0 && Intersect.y == 0 )
				{
					Intersect.x = -32 + gf_mtRand() % 64;
					Intersect.y = -32 + gf_mtRand() % 64;
					HOSAKA_PRINT("Intersect Random !!! \n");
				}

				VEC_Normalize(&Intersect, &Intersect);

				bHitResult1 = CalcPokeHit( pRPoke, pTar, &Intersect );
				Intersect.x *= -1;
				Intersect.y *= -1;
				bHitResult2 = CalcPokeHit( pTar, pRPoke, &Intersect );

				// 状態遷移
				{
					POKE_STATE_CHG chg1 = GetStateByHitting( pRPoke );
					POKE_STATE_CHG chg2 = GetStateByHitting(  pTar );
					SetStateByHitting( pRPoke, chg1 );
					SetStateByHitting( pTar, chg2 );
				}

				// どちらかがヒットした場合
				if( bHitResult1 || bHitResult2 )
				{
					// ヒットフラグON
					Invalid[i][j] = Invalid[j][i] = 1;
					HOSAKA_PRINT("Invalid ON [%d][%d]\n",i, j );

#ifdef DEBUG_DUMP_STATE
					// 主人公の状態を記録(当たった瞬間にデバッグボタンを押していると表示)
					_debug_dump_state( debug_pre_state, debug_pre_state2, pRPoke->State, pTar->State );
#endif
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
/**
 *	@brief	スタミナ回復処理
 *
 *	@param	DROP_TEAM_DATA_PTR pTeamDataHead
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCntStaminaProc( DROP_TEAM_DATA_PTR pTeamDataHead, PKTHLN_PTR work )
{
	int tc, pc;

	for( tc=0; tc<ENTRY_MEMBER_NUM_MAX; tc++ )
	{
		for( pc=0; pc<ENTRY_MAX; pc++ )
		{
			DROP_TEAM_DATA_PTR pTeamData;
			RING_POKE* pRPoke;
			DROP_POKE* pDPoke;
			fx32 fxStaminaMax;
			f32 fxHealPoint = 0;

			pTeamData = &pTeamDataHead[ tc ];
			pRPoke = &pTeamData->RingPoke;
			pDPoke = &pTeamData->DropPoke[ pc ];
			fxStaminaMax = pDPoke->cpp->fxStaminaMax;

			if( pRPoke->PokeID == pc )
			{
				if( pRPoke->bPanic )
				{
					// 混乱終了判定
					if( --pRPoke->PanicFrame <= 0 )
					{
						pDPoke->fxStamina = PANIC_RECVER_P_FX;
						pRPoke->LastPos = pRPoke->NowPos;
						pRPoke->bPanic = 0;
						HOSAKA_PRINT("Panic End !\n");
					}
				}
				// スタミナが切れていたらピヨりカウンタを回し始める
				else if( pDPoke->fxStamina <= 0 )
				{
					pRPoke->PanicFrame = PANIC_SYNC;
					pRPoke->bPanic = 1;
					pRPoke->bJumpPressStart = 0;
					pRPoke->bTackleStart = 0;

					// AP:失敗回数
					PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_FAIL, 1 );

					HOSAKA_PRINT("Panic Start ...\n");
				}
				else
				{
					// リングポケモン回復
					fxHealPoint = STAMINA_HEAL_RING_FX;
				}
			}
			else
			{
				// ベンチポケモン回復
				fxHealPoint = STAMINA_HEAL_BENCH_FX;
			}

			pDPoke->fxStamina += fxHealPoint;

			if( pDPoke->fxStamina > fxStaminaMax )
			{
				pDPoke->fxStamina = fxStaminaMax;
			}
		}
	}
}


//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン処理(チーム一括)
 *
 *	@param	DROP_TEAM_DATA_PTR pTeamDataHead
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_Calc( DROP_TEAM_DATA_PTR pTeamDataHead, PKTHLN_PTR work )
{
	int i,j;

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		DROP_TEAM_DATA_PTR pTeamData = &pTeamDataHead[i];
		TP_STATE_DROP* TpState = &pTeamData->TpState;
		RING_POKE* pRPoke = &pTeamData->RingPoke;
		VecFx32 vecTouch = {0};
		VecFx32 vecTrg = {0};

		vecTouch.x = FX32_CONST( pTeamData->TouchPos.X );
		vecTouch.y = FX32_CONST( pTeamData->TouchPos.Y );
		vecTrg.x = FX32_CONST( pTeamData->TrgPos.X );
		vecTrg.y = FX32_CONST( pTeamData->TrgPos.Y );

		//-----------------------------------------------------------------------------------------------------
		// タッチ操作
		//-----------------------------------------------------------------------------------------------------
		// TRG判定
		if( TpState->Trg )
		{
			// 交代ボタンチェック
			if( CheckChangeButton( pTeamData->TouchPos.X, pTeamData->TouchPos.Y ) )
			{
				// 交代不可状態チェック
				if( Poke_CheckChangeEnable( pRPoke ) )
				{
					Change_Start( pRPoke );
				}
			}
			else
			{
				// ポケモンタッチチェック
				fx32 fx_range = FX32_CONST( PTFrame_GetPokeTouchRange() );
				if( pRPoke->bChange == FALSE && pRPoke->State != POKE_ST_FALL && Check_HitVec( &pRPoke->NowPos, &vecTouch, fx_range ) )
				{
					TpState->bLockOn = 1;
					HOSAKA_PRINT("[%d] Touch lock on \n", i);
				}
			}
		}
		// CONT判定
		else if( TpState->Cont )
		{
			// ロックオン中
			if( TpState->bLockOn == 1 )
			{
				s16 now_x = pRPoke->NowPos.x >> FX32_SHIFT;
				s16 now_y = pRPoke->NowPos.y >> FX32_SHIFT;


				// 落下したらロックオンを切る
				if( pRPoke->State == POKE_ST_FALL )
				{
					TpState->bLockOn = 0;
				}
				else
				// アソビ
				if( MATH_IAbs( pTeamData->TouchPos.X - now_x ) > POKE_TOUCH_PLAY ||
					MATH_IAbs( pTeamData->TouchPos.Y - now_y ) > POKE_TOUCH_PLAY )
				{
					// 移動先更新
					Poke_SetLastPos( pRPoke, &pTeamData->TouchPos );
				}
			}
		}
		// リリース
		else if( TpState->ReleaseCount > 0 )
		{
			// ロックオン中
			if( TpState->bLockOn == 1 &&
				( pRPoke->State == POKE_ST_ALIVE || pRPoke->State == POKE_ST_REVIVE ) &&
				pRPoke->bPanic == 0 && pRPoke->bChange == 0 )
			{
				// ジャンプ判定
				if( Check_HitVec( &vecTrg, &vecTouch, JUMP_TP_IN_FX ) )
				{
					HOSAKA_PRINT("Jump ReleaseCount:%d \n",TpState->ReleaseCount);
					if( TpState->ReleaseCount <= JUMP_CHECK_SYNC )
					{
						// ジャンプ予約
						pRPoke->bTackleStart = 0;
						pRPoke->bJumpPressStart = 1;
					}
				}
				// タックル判定
				else if( Check_HitVec( &vecTrg, &vecTouch, TACKLE_TP_OUT_FX ) == FALSE )
				{
					HOSAKA_PRINT("Tackle ReleaseCount:%d \n",TpState->ReleaseCount);
					if( TpState->ReleaseCount <= TACKLE_CHECK_SYNC )
					{
						// タックル予約
						pRPoke->bTackleStart = 1;
						pRPoke->bJumpPressStart = 0;
						pRPoke->TackleTarPos = pTeamData->TouchPos;
					}
				}
			}

			TpState->bLockOn = 0;
		}

		// ポケモン処理
		Poke_Calc( &pTeamData->RingPoke, work );
	}

	// ポケモンヒット処理
	TeamCnt_CalcPokeCollision( work );
	// ポケモンスタミナ回復処理
	TeamCntStaminaProc( pTeamDataHead, work );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	通信データを設定
 *
 *	@param	DROP_SERVER_WORK* pServer
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void TeamCnt_SetCommData( DROP_SERVER_WORK* pServer, PKTHLN_PTR work )
{
	int i;
	COMM_EVENT_COMMON_DATA* data;
	DROP_COMM_PARENT_PTR parent;
	BOOL bHitSndNormal = 0;

	data = PTFrame_GetEvtComCmnData(work);
	parent = PKTHLN_CommPack_GetData(&data->SendShareData);

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		RING_POKE* pRPoke = &pServer->TeamData[i].RingPoke;
		s16 px = pRPoke->NowPos.x >> FX32_SHIFT;
		s16 py = pRPoke->NowPos.y >> FX32_SHIFT;

		// 指定回数送信したらクリアする
		if( pRPoke->HitSendCnt > 0 )
		{
			pRPoke->HitSendCnt--;
		}
		else if( pRPoke->HitSendCnt == 0 )
		{
			pRPoke->HitNetID = 0;
			pRPoke->HitType = HIT_EFF_TYPE_NULL;
		}

		// ヒット情報を送信
		parent->Team[i].HitNetID = pRPoke->HitNetID;
		parent->Team[i].HitType = pRPoke->HitType;

		parent->Team[i].Active = pRPoke->PokeID;
		parent->Team[i].Dir = pRPoke->Dir - 1;
		parent->Team[i].PokeX = MATH_CLAMP( px, 0, 255 );
		parent->Team[i].PokeY = MATH_CLAMP( py, 0, 255 );
		parent->Team[i].PokeZ = pRPoke->fxPosZ >> FX32_SHIFT;
		parent->Team[i].State = pRPoke->State;
		parent->Team[i].Score = pRPoke->Score;
		parent->Team[i].ChgState = pRPoke->ChgState;

		parent->Team[i].BtnSt = pRPoke->BtnSt;

		// 一匹でもノーマル衝突SEを鳴らしたいポケモンがいた場合は通信データを飛ばす
		if( pRPoke->bHitSndNormal )
		{
			bHitSndNormal = 1;

			// ノーマル衝突は毎SYNCクリア
			// 親機から子機への情報が1SYNCでも欠落した場合発音されないが、仕様とする。
			pRPoke->bHitSndNormal = 0;
		}

#if 0
		HOSAKA_PRINT("[%d]set comm:{%d,%d} = {%d,%d} tar{%d,%d}\n", i,
				parent->Team[i].PokeX, parent->Team[i].PokeY,
				pRPoke->NowPos.x >> FX32_SHIFT,
				pRPoke->NowPos.y >> FX32_SHIFT,
				pRPoke->LastPos.x >> FX32_SHIFT,
				pRPoke->LastPos.y >> FX32_SHIFT );
#endif
	}

	// ノーマル衝突SE
	parent->bHitSndNormal = bHitSndNormal;

	// 12匹分必要なデータ
	// スタミナを転送
	parent->StateBit = 0;

	for( i=0; i<ACT_POKE_MAX; i++ )
	{
		POKE_STA_STATE State;
		DROP_POKE* pDPoke = &pServer->TeamData[i/ENTRY_MAX].DropPoke[i%ENTRY_MAX];
		int Point = pDPoke->fxStamina >> FX32_SHIFT;

		if( Point == 0 )
		{
			State = POKE_STA_ST_PANIC;
		}
		else if( Point < STAMINA_SWEAT_POINT )
		{
			State = POKE_STA_ST_SWEAT;
		}
		else
		{
			State = POKE_STA_ST_NORMAL;
		}

		parent->StateBit += State << i*2;
	}

#ifdef PM_DEBUG
	// データ漏れ検出
	GF_ASSERT_MSG( parent->padding[0] == 0, "send padding[0]=%d",parent->padding[0] );
	GF_ASSERT_MSG( parent->padding[1] == 0, "send padding[1]=%d",parent->padding[1] );
	GF_ASSERT_MSG( parent->padding[2] == 0, "send padding[2]=%d",parent->padding[2] );
	GF_ASSERT_MSG( parent->padding[3] == 0, "send padding[3]=%d",parent->padding[3] );
	GF_ASSERT_MSG( parent->DuumyBit1 == 0, "send DuumyBit1=%d", parent->DuumyBit1 );
	GF_ASSERT_MSG( parent->DuumyBit2 == 0, "send DummyBit2=%d", parent->DuumyBit2 );
#endif
}

//-----------------------------------------------------------------------------
/**
 *	@brief	アフィン座標設定
 *
 *	@param	POKE_ACT_PTR poke_act
 *	@param	bCenter	TRUE:ポケモンの中心 FALSE:足元
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_SetAffineVec( POKE_ACT_PTR poke_ptr, BOOL bCenter )
{
	const MONS_INFO *info;
	PCLACT_PTR	pclact_ptr = PokeAct_GetPclActPtr(poke_ptr);
	int space = PCLACT_GetSpaceHeight(pclact_ptr);
	int size;
	VecFx32 aff_vec = {0,0,0};

	info = PCLACT_GetMonsInfo(pclact_ptr);

	if (info->CellType)
	{
		size = POKE_L_SIZE;
	}
	else
	{
		size = POKE_S_SIZE;
	}

	if( bCenter )
	{
		aff_vec.x = (size/2)*FX32_ONE;
		aff_vec.y = (space+(size-space)/2)*FX32_ONE;
	}
	else
	{
		aff_vec.x = (size/2)*FX32_ONE;
		aff_vec.y = (size)*FX32_ONE;
	}

	//アフィン座標セット
	PokeAct_SetAffineVec(poke_ptr, &aff_vec);
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンのState遷移
 *
 *	@param	RING_POKE* pRPoke
 *	@param	State
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_SetState( RING_POKE* pRPoke, POKE_STATE State )
{
	if( State == POKE_ST_REVIVE )
	{
		pRPoke->ReviveFrame = RIVIVE_SYNC;
	}
	else
	{
		pRPoke->ReviveFrame = 0;
	}

	if( State == POKE_ST_TACKLE_END )
	{
		pRPoke->TackleFrame = TACKLE_END_SYNC;
	}

	pRPoke->State = State;
}

//----------------------------------------------------------------------------
/**
 *	@brief	ポケモン同士の当たり判定
 *
 *	@param	* poke1	ポケ1
 *	@param	* poke2	ポケ2
 *
 *	@return	TRUEでヒット
 */
//-----------------------------------------------------------------------------
static BOOL Poke_CheckCircle(RING_POKE * poke1, RING_POKE * poke2)
{
	int r1;
	int r2;
	fx32 len;
	VecFx32 vec, vec1, vec2;
	fx32 range;

	// 高度が足りなかったらFALSE
	if( MATH_IAbs( poke1->fxPosZ - poke2->fxPosZ ) > FX32_CONST(5) )
	{
//		HOSAKA_PRINT("[%d]Z:%f -> [%d]z:%f 高度が足りない\n",
//				poke1->NetID, FX_FX32_TO_F32(poke1->fxPosZ),
//				poke2->NetID, FX_FX32_TO_F32(poke2->fxPosZ) );
		return FALSE;
	}

	PokeAct_GetHitData(poke1->DropPoke->PokeActPtr, poke1->NowPos.x, poke1->NowPos.y, &vec1.x, &vec1.y, &r1);
	PokeAct_GetHitData(poke2->DropPoke->PokeActPtr, poke2->NowPos.x, poke2->NowPos.y, &vec2.x, &vec2.y, &r2);

	// タックルは当たり判定を大きめに取る
	if( poke1->State == POKE_ST_TACKLE ){ r1 += TACKLE_RANGE_ADD; }
	if( poke2->State == POKE_ST_TACKLE ){ r2 += TACKLE_RANGE_ADD; }

	range = (r1+r2)*FX32_ONE;

	vec1.z = 0;
	vec2.z = 0;

	VEC_Subtract( &vec1, &vec2, &vec );
	//方向ベクトルの長さを取得
	len = VEC_Mag(&vec);

	if (len <= range){
		// 有効範囲
		return TRUE;
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンが交代可能か判定
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Poke_CheckChangeEnable( RING_POKE* pRPoke )
{
	if( Poke_CheckTouchEnable( pRPoke ) )
	{
		fx32 range;
		VecFx32 post = {0};
		u8 NetID = pRPoke->NetID;

		range = g_Post[ NetID ].circle * FX32_ONE;
		post.x = g_Post[ NetID ].px * FX32_ONE;
		post.y = g_Post[ NetID ].py * FX32_ONE;

		return Check_HitVec( &pRPoke->NowPos, &post, range );
	}

	return FALSE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンが操作可能状態か？
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Poke_CheckTouchEnable( RING_POKE* pRPoke )
{
	// 押されている時は操作不能
	if( pRPoke->bPush ){ return FALSE; }
	// 混乱
	if( pRPoke->bPanic ){ return FALSE; }
	// 交代
	if( pRPoke->bChange ){ return FALSE; }

	switch( pRPoke->State )
	{
		// 操作可能
		case POKE_ST_ALIVE :
//		case POKE_ST_HIT_WAIT :
			return TRUE;
		default :
			return FALSE;
	}

	return FALSE; ///<ダミー
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの目的地更新
 *
 *	@param	RING_POKE* pRPoke
 *	@param	Pos
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_SetLastPos( RING_POKE* pRPoke, const POS_DATA* Pos )
{
	pRPoke->LastPos.x = Pos->X * FX32_ONE;
	pRPoke->LastPos.y = Pos->Y * FX32_ONE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	正規化されたベクトルから方向を算出
 *
 *	@param	VecFx32* norm_dir_vec
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static u8 Poke_CalcDir( VecFx32* norm_dir_vec )
{
	// ZX平面
	u16 rad;
	VecFx32 vec = {0,0,FX32_ONE};
	VecFx32 xz_vec = {0};
	u8 dir;

	xz_vec.x = norm_dir_vec->y;
	xz_vec.z = norm_dir_vec->x;
	xz_vec.y = 0;

	rad = GetRad(&vec, &xz_vec);

	if ( (0<=rad&&rad<=0x2000)||(0xe000<=rad) ){	//右
		dir = POKE_DIR_RIGHT;
	}else if(0x2000<rad&&rad<0x6000){		//下
		dir = POKE_DIR_DOWN;
	}else if(0x6000<=rad&&rad<=0xa000){		//左
		dir = POKE_DIR_LEFT;
	}else{	//(0xa000<rad&&rad<0xe000)		//上
		dir = POKE_DIR_UP;
	}

	return dir;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモンの衝突判定が有効か？
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static BOOL Poke_IsHitActive( RING_POKE* pRPoke )
{
	GF_ASSERT(pRPoke);
	GF_ASSERT(pRPoke->State != POKE_ST_NONE);

	return ( pRPoke->State != POKE_ST_JUMP_WAIT && !pRPoke->bChange && pRPoke->State != POKE_ST_REVIVE && pRPoke->State != POKE_ST_FALL );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	対象ＩＤの衝突フラグをＯＦＦ
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Invalid_Off( PKTHLN_PTR work, u8 NetID )
{
	int i;
	PKTHLN_DROP_PTR ptr;
	u8 (*Invalid)[ENTRY_MEMBER_NUM_MAX]; ///< 二次元配列へのポインタ

	ptr = PTFrame_GetEventWorkPtr(work);
	Invalid = ptr->wkServer.PokeInvalidCollision;	///< 処理フラグ

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		Invalid[NetID][i] = 0;
		// ・相手のフラグを折らなくて良いのか？
		// ・これを折ると同時ヒットがおこりにくくなる？OFFなのに当たりにくくなるなんてことはあるのか？
//		Invalid[i][NetID] = 0;
	}
	HOSAKA_PRINT("Invalid OFF [%d] All\n", NetID );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	リングポケモン移動
 *
 *	@param	RING_POKE* pRPoke
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_Move( RING_POKE* pRPoke, PKTHLN_PTR work )
{
	BOOL bMove = FALSE;
	fx32 move;
	fx32 len;
	VecFx32 dir_vec;
	VecFx32 norm_dir_vec;
	VecFx32 move_vec;

	// 方向ベクトル取得
	VEC_Subtract( &pRPoke->LastPos, &pRPoke->NowPos, &dir_vec );
	// 方向ベクトルの長さを取得
	len = VEC_Mag(&dir_vec);

	// 移動フラグを算出
	bMove = (len > 0);

	//--------------------------------------------------------
	// ふっとび・ふんばり処理
	//--------------------------------------------------------
	if( pRPoke->bPush )
	{
		VecFx32 vecHold;
		fx32 fxHold;

		GF_ASSERT( pRPoke->HitState );

		// 通常のふんばりと、タックル・プレスのふんばりを分ける
		if( pRPoke->HitState == POKE_ST_TACKLE || pRPoke->HitState == POKE_ST_JUMP )
		{
			fxHold = POKE_HOLD_L_FX;
		}
		else
		{
			fxHold = POKE_HOLD_S_FX;
		}

		// HitWait中はふんばらない
		if( pRPoke->HitWait <= 0 )
		{
			VecFx32 dirNorm = {0};
			VecFx32 vecDmy = {0};

			// ふっとばされている方向を検出
			VEC_Normalize(&pRPoke->PushVec, &dirNorm);

			// ふんばり力を分解
			VEC_MultAdd( fxHold, &dirNorm, &vecDmy, &vecHold );

			vecHold.x = MATH_IAbs(vecHold.x);
			vecHold.y = MATH_IAbs(vecHold.y);

			VEC_ZeroConverge( &pRPoke->PushVec, vecHold.x, vecHold.y );
		}

		VEC_Add( &pRPoke->NowPos, &pRPoke->PushVec, &pRPoke->NowPos );

#ifdef PM_DEBUG
#ifdef DEBUG_PRINT_PUSH
		if( pRPoke->PushVec.x != 0 || pRPoke->PushVec.y != 0 )
		{
			HOSAKA_PRINT("[%d,%d] HitState:%d HitWait:%d PushVec:{%f, %f} ",
				pRPoke->NetID, pRPoke->PokeID,
				pRPoke->HitState, pRPoke->HitWait,
				FX_FX32_TO_F32(pRPoke->PushVec.x),
				FX_FX32_TO_F32(pRPoke->PushVec.y) );

			if( pRPoke->HitWait <= 0 )
			{
				HOSAKA_PRINT("Hold{%f, %f} ",
					FX_FX32_TO_F32(vecHold.x),
					FX_FX32_TO_F32(vecHold.y) );
			}

			HOSAKA_PRINT("\n");
		}
#endif
#endif

		// 目的地更新
		pRPoke->LastPos = pRPoke->NowPos;

		// プッシュ終了判定
		if( pRPoke->PushVec.x == 0 && pRPoke->PushVec.y == 0 )
		{
			Invalid_Off( work, pRPoke->NetID );

			pRPoke->PushPoke = NULL;	///< 誰に押されたか忘れる
			pRPoke->bPush = 0;
		}
		else
		{
			// プッシュ中は処理を打ち切る
			return;
		}
	}

	// 以下の条件で処理を打ち切る
	// ・混乱中
	// ・ヒットウェイト中
	// ・動いていない時
	if( pRPoke->bPanic || pRPoke->HitWait > 0 || bMove == FALSE )
	{
		return;
	}

	//--------------------------------------------------------
	// 方向変更・移動処理
	//--------------------------------------------------------
	// 正規化
	VEC_Normalize(&dir_vec, &norm_dir_vec);

	// ノックバック時に向きを変更しなくする
	if( pRPoke->bPush == 0 )
	{
		// 方向ベクトルから向きを決定
		pRPoke->Dir = Poke_CalcDir( &norm_dir_vec );
	}

	move = pRPoke->DropPoke->cpp->fxMove;

	// 疲労状態なら速度半減
	if( pRPoke->DropPoke->fxStamina < STAMINA_SLOW_POINT * FX32_ONE )
	{
		move = FX_Mul( move, STAMINA_SLOW_MUL_FX );
	}

	// 方向ベクトルの長さが、スピード長以下ならば速度係数を無視
	// 誤差修正
	if( len <= move )
	{
		move_vec = dir_vec;
	}
	else
	{
		// スピード調整
		VecFx32 add = {0,0,0};
		VEC_MultAdd( move, &norm_dir_vec, &add, &move_vec);
	}

	// 通常移動
	VEC_Add( &pRPoke->NowPos, &move_vec, &pRPoke->NowPos);

	// 正規化された方向ベクトルを保持
	pRPoke->NormDirVec = norm_dir_vec;
}


//-----------------------------------------------------------------------------
/**
 *	@brief	落下判定
 *
 *	@param	DROP_POKE* pRPoke
 *
 *	@retval	TRUE:落下した
 */
//-----------------------------------------------------------------------------
static BOOL Poke_CheckFall( RING_POKE* pRPoke )
{
	enum {
		POS_Y_U = RING_Y_MIN,
		POS_Y_D = RING_Y_MAX,
	};

	static const s16 PosTbl[][2] = {
		// -- 左辺 --
		{  8*4, POS_Y_D },	//下
		{  8*6, POS_Y_U },	//上
		// -- 右辺 --
		{  8*26, POS_Y_U },	//上
		{  8*28, POS_Y_D },	//下
	};

	int i;
	int poke_x, poke_y;

	// 回り込んでいたら落下したことにする
	if( pRPoke->NowPos.x <= 0 || pRPoke->NowPos.y <= 0 )
	{
		return TRUE;
	}

	// タッチ座標から影座標を算出
	PokeAct_GetShadowPosByTouchPos(pRPoke->DropPoke->PokeActPtr,
			pRPoke->NowPos.x>>FX32_SHIFT, pRPoke->NowPos.y>>FX32_SHIFT,
			&poke_x, &poke_y );

	// 上下判定
	if( (poke_y >= POS_Y_U) && (poke_y <= POS_Y_D) )
	{
		// 左辺、右辺の判定(線分と点の当たり判定)
		VecFx32 VecPoke, Vec[4];

		// ポケモン座標
		VecPoke.x = FX32_CONST( poke_x );
		VecPoke.y = FX32_CONST( poke_y );

		// 座標変換
		for( i=0; i<4; i++ )
		{
			Vec[i].x = PosTbl[i][0] * FX32_ONE;
			Vec[i].y = PosTbl[i][1] * FX32_ONE;
		}

		{
			fx32 valLeft;
			fx32 valRight;

			// 左辺のY軸について計算
			valLeft = FX_Mul( Vec[1].y - Vec[0].y, VecPoke.x - Vec[0].x );
			valLeft = FX_Div( valLeft, Vec[1].x - Vec[0].x );
			valLeft += Vec[0].y;

			// 右辺のY軸について計算
			valRight = FX_Mul( Vec[3].y - Vec[2].y, VecPoke.x - Vec[2].x );
			valRight = FX_Div( valRight, Vec[3].x - Vec[2].x );
			valRight += Vec[2].y;

			// 正方形、左辺、右辺の当たり判定
			if(	VecPoke.y >= valLeft && VecPoke.y >= valRight )
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ポケモン復活
 *
 *	@param	DROP_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_Revive( RING_POKE* pRPoke )
{
	// 落ちたポケモンのスタミナを全快
	pRPoke->DropPoke->fxStamina = pRPoke->DropPoke->cpp->fxStaminaMax;
	// ID操作
	PokeID_Add( pRPoke );
	// リセット
	Poke_Reset( pRPoke );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	リングポケモンのデータをリセット
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_Reset( RING_POKE* pRPoke )
{
	int sx, sy;
	int tx, ty;

	sx = g_PokeInitPos[ pRPoke->NetID ].X;
	sy = g_PokeInitPos[ pRPoke->NetID ].Y;

	PokeAct_GetTouchPosByShadowPos( pRPoke->DropPoke->PokeActPtr, sx, sy, &tx, &ty );
	pRPoke->NowPos.x = tx * FX32_ONE;
	pRPoke->NowPos.y = ty * FX32_ONE;
	pRPoke->LastPos.x = tx * FX32_ONE;
	pRPoke->LastPos.y = ty * FX32_ONE;
	pRPoke->PushVec = (VecFx32){0,0,0};
	pRPoke->Dir = POKE_DIR_DOWN;
	pRPoke->bPush = 0;
	pRPoke->bJumpPressStart = 0;
	pRPoke->bTackleStart = 0;

	HOSAKA_PRINT("[%d] ring poke data reset\n", pRPoke->NetID);
}

//-----------------------------------------------------------------------------
/**
 *	@brief	重力計算
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_CalcGravity( RING_POKE* pRPoke )
{
	// 重力の影響を受けない。
	// ジャンプ中、ジャンプウェイト中、プレス中
	if( pRPoke->State == POKE_ST_JUMP ||
		pRPoke->State == POKE_ST_JUMP_WAIT ||
		pRPoke->State == POKE_ST_PRESS )
	{
		return;
	}

	// 落下終了
	if( pRPoke->fxPosZ <= 0 )
	{
		pRPoke->fxJumpG = JUMP_DEF_ACCEL_FX;
	}
	else
	{
		// 落下
		pRPoke->fxPosZ -= pRPoke->fxJumpG;

		if( pRPoke->fxPosZ < 0 )
		{
			pRPoke->fxPosZ = 0;
		}

		pRPoke->fxJumpG += JUMP_GRAVITY_FX;
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	個別ポケモン処理
 *
 *	@param	RING_POKE* pRPoke
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Poke_Calc( RING_POKE* pRPoke, PKTHLN_PTR work )
{
	// ヒットストップ
	if( pRPoke->HitStopFrame > 0 )
	{
		pRPoke->HitStopFrame--;
//		HOSAKA_PRINT("[%d,%d] HitStop:%d \n",pRPoke->NetID, pRPoke->PokeID, pRPoke->HitStopFrame);
		if( pRPoke->HitStopFrame > 0 ){	return; }
	}

	// 移動処理
	switch( pRPoke->State )
	{
		// 移動処理なし
		case POKE_ST_JUMP_IN :
		case POKE_ST_JUMP_WAIT :
		case POKE_ST_PRESS :
		case POKE_ST_PRESS_END :
		case POKE_ST_TACKLE_IN :
		case POKE_ST_TACKLE_END :
			break;

		// タックルは移動処理をフック
		case POKE_ST_TACKLE :
			Tackle_Move( pRPoke );
			break;

		// それ以外は通常移動
		default : Poke_Move( pRPoke, work );
	}

	switch( pRPoke->State )
	{
	case POKE_ST_REVIVE :
		// 押されるはずがない
		GF_ASSERT( pRPoke->bPush == 0 );

		// 落下チェック
		if( Poke_CheckFall( pRPoke ) )
		{
			Fall_Start( pRPoke );
		}
		else if( pRPoke->bJumpPressStart )
		{
			// ジャンプ処理開始
			JumpPress_Start( pRPoke );
		}
		else if( pRPoke->bTackleStart )
		{
			// タックル処理開始
			Tackle_Start( pRPoke, &pRPoke->TackleTarPos );
		}
		else if( --pRPoke->ReviveFrame <= 0 )
		{
			Poke_SetState( pRPoke, POKE_ST_ALIVE );
			HOSAKA_PRINT("[%d,%d] Poke revive.\n",pRPoke->NetID, pRPoke->PokeID );
		}
		break;

	case POKE_ST_FALL :
		if( --pRPoke->FallFrame <= 0 )
		{
			// 通信データの関係上、
			// 状態フラグ以外はこの段階で復帰時の値を渡しておく
			Poke_Revive( pRPoke );
			Poke_SetState( pRPoke, POKE_ST_REVIVE );
		}
		break;

	case POKE_ST_HIT_WAIT :
		// 落下チェック
		if( Poke_CheckFall( pRPoke ) )
		{
			Fall_Start( pRPoke );
		}
		else if( --pRPoke->HitWait <= 0 )
		{
			Poke_SetState( pRPoke, POKE_ST_ALIVE );
		}
		break;

	case POKE_ST_ALIVE :
		// 落下チェック
		if( Poke_CheckFall( pRPoke ) )
		{
			Fall_Start( pRPoke );
		}
		else if( pRPoke->bPush )
		{
			// 衝突中は行動開始しない
		}
		else if( pRPoke->bJumpPressStart )
		{
			// ジャンプ処理開始
			JumpPress_Start( pRPoke );
		}
		else if( pRPoke->bTackleStart )
		{
			// タックル処理開始
			Tackle_Start( pRPoke, &pRPoke->TackleTarPos );
		}
		break;

	//-------------------------------------------------------
	// JUMP & PRESS
	//-------------------------------------------------------
	case POKE_ST_JUMP_IN :
		if( --pRPoke->JumpFrame <= 0 )
		{
			pRPoke->JumpFrame = 0;
			Poke_SetState( pRPoke, POKE_ST_JUMP );
		}
		break;

	case POKE_ST_JUMP :
		// ジャンプ処理
		{
			u8 Level = pRPoke->DropPoke->cpp->JumpLevel;
			pRPoke->fxPosZ = g_JumpZ[ Level ][ pRPoke->JumpFrame ] * FX32_ONE;
		}

		pRPoke->JumpFrame++;

		if( pRPoke->JumpFrame >= JUMP_SYNC )
		{
			pRPoke->JumpFrame = JUMP_WAIT_SYNC;
			Poke_SetState( pRPoke, POKE_ST_JUMP_WAIT );
		}
		break;

	case POKE_ST_JUMP_WAIT :
		// ジャンプ一時停止
		if( --pRPoke->JumpFrame <= 0 )
		{
			pRPoke->JumpFrame = 0;
			Poke_SetState( pRPoke, POKE_ST_PRESS );
		}
		break;

	case POKE_ST_PRESS :

		// 0になっていたら終了
		if( pRPoke->fxPosZ == 0 )
		{
			JumpPress_End( pRPoke );

			// 落下チェック
			if( Poke_CheckFall( pRPoke ) )
			{
				Fall_Start( pRPoke );
			}
			break;
		}

		// 等速落下
		{
			u8 Level = pRPoke->DropPoke->cpp->JumpLevel;
			pRPoke->fxPosZ -= g_PressZ[ Level ] * FX32_ONE;

			// プレス処理
			if( pRPoke->fxPosZ <= 0 )
			{
				// Z座標0で1フレーム当たり判定を持たせる
				pRPoke->fxPosZ = 0;
			}

			HOSAKA_PRINT("Pressing! fxPosZ:%f g_PressZ[%d]:%d \n",
					FX_FX32_TO_F32(pRPoke->fxPosZ), Level, g_PressZ[Level]);
		}
		break;

	case POKE_ST_PRESS_END :
		if( --pRPoke->JumpFrame <= 0 )
		{
			Poke_SetState( pRPoke, POKE_ST_ALIVE );
		}
		break;

	//-------------------------------------------------------
	// TACKLE
	//-------------------------------------------------------
	case POKE_ST_TACKLE_IN :
		if( --pRPoke->TackleFrame <= 0 )
		{
			pRPoke->TackleFrame = TACKLE_MAIN_SYNC;
			Poke_SetState( pRPoke, POKE_ST_TACKLE );
		}
		break;

	case POKE_ST_TACKLE :
		if( --pRPoke->TackleFrame <= 0 )
		{
			// 目的地更新
			pRPoke->LastPos = pRPoke->NowPos;
			pRPoke->PushVec = (VecFx32){0,0,0};

			if( TACKLE_END_SYNC <= 0 )
			{
				Tackle_End( pRPoke );
			}
			else
			{
				// タックルウェイトへ
				Poke_SetState( pRPoke, POKE_ST_TACKLE_END );
			}
		}
		break;

	case POKE_ST_TACKLE_END :
		if( --pRPoke->TackleFrame <= 0 )
		{
			Tackle_End( pRPoke );
		}
		break;

	default : GF_ASSERT(0);
	}

	// 重力演算
	Poke_CalcGravity( pRPoke );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	体当たり開始処理
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Tackle_Start( RING_POKE* pRPoke, POS_DATA* pTouchPos )
{
	VecFx32 vecTar = {0};
	fx32 fxPush = TACKLE_PUSH_FX;

	GF_ASSERT( pRPoke->State == POKE_ST_ALIVE || pRPoke->State == POKE_ST_REVIVE );
	GF_ASSERT( pRPoke->bPanic == 0 );
	GF_ASSERT( pRPoke->bChange == 0 );

	pRPoke->bTackleStart = 0;

	vecTar.x = pTouchPos->X << FX32_SHIFT;
	vecTar.y = pTouchPos->Y << FX32_SHIFT;

	// 方角を確定
	VEC_Subtract( &vecTar, &pRPoke->NowPos, &pRPoke->NormDirVec);
	VEC_Normalize(&pRPoke->NormDirVec, &pRPoke->NormDirVec);

	// 初速を決定
	VEC_MultAdd( fxPush, &pRPoke->NormDirVec, &pRPoke->NormDirVec, &pRPoke->PushVec );

	// 失敗判定
	if( pRPoke->PushVec.x == 0 && pRPoke->PushVec.y == 0  )
	{
		HOSAKA_PRINT("Tackle Failure ... \n");
	}
	else
	{
		pRPoke->Dir = Poke_CalcDir( &pRPoke->NormDirVec );
		pRPoke->TackleFrame = TACKLE_PRE_SYNC;
		Poke_SetState( pRPoke, POKE_ST_TACKLE_IN );
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	体当たり終了処理
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Tackle_End( RING_POKE* pRPoke )
{
	GF_ASSERT( pRPoke->State == POKE_ST_TACKLE_END || pRPoke->State == POKE_ST_TACKLE );

	// 停止
	pRPoke->PushVec = (VecFx32){0,0,0};
	pRPoke->LastPos = pRPoke->NowPos;

	Poke_SetState( pRPoke, POKE_ST_ALIVE );

	// 落下チェック
	if( Poke_CheckFall( pRPoke ) )
	{
		Fall_Start( pRPoke );
	}

	HOSAKA_PRINT("tackle end\n");
}

//-----------------------------------------------------------------------------
/**
 *	@brief	体当たり処理
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Tackle_Move( RING_POKE* pRPoke )
{
	VecFx32 vecHold = { TACKLE_ADD_VEC, TACKLE_ADD_VEC, 0 };

	HOSAKA_PRINT("tackle move_vec : {%f,%f} total=%f \n",
			FX_FX32_TO_F32(pRPoke->PushVec.x),
			FX_FX32_TO_F32(pRPoke->PushVec.y),
			FX_FX32_TO_F32( MATH_IAbs( pRPoke->PushVec.x ) + MATH_IAbs( pRPoke->PushVec.y ) ) );

	{
		VecFx32 vecAdd;
		VecFx32 vecDmy = {0};

		VEC_MultAdd( TACKLE_ADD_VEC, &pRPoke->NormDirVec, &vecDmy, &vecAdd );
		VEC_Add( &pRPoke->PushVec, &vecAdd, &pRPoke->PushVec );
	}

	VEC_Add( &pRPoke->NowPos, &pRPoke->PushVec, &pRPoke->NowPos);
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ジャンププレス 開始
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void JumpPress_Start( RING_POKE* pRPoke )
{
	GF_ASSERT( pRPoke->State == POKE_ST_ALIVE || pRPoke->State == POKE_ST_REVIVE );
	GF_ASSERT( pRPoke->bPanic == 0 );
	GF_ASSERT( pRPoke->bChange == 0 );

	pRPoke->bJumpPressStart = 0;
	pRPoke->JumpFrame = JUMP_IN_SYNC;

	if( pRPoke->DropPoke->cpp->JumpLevel )
	{
		// AP:ジャンプ回数
		PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_JUMP, 1 );
	}

	Poke_SetState( pRPoke, POKE_ST_JUMP_IN );

	HOSAKA_PRINT("Jump In\n");
}

//-----------------------------------------------------------------------------
/**
 *	@brief	ジャンププレス 終了開始
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void JumpPress_End( RING_POKE* pRPoke )
{
	Poke_SetState( pRPoke, POKE_ST_PRESS_END );
	pRPoke->JumpFrame = PRESS_END_SYNC;
	pRPoke->fxPosZ = 0;
	pRPoke->LastPos = pRPoke->NowPos;
	HOSAKA_PRINT("Jump End\n");
}

//-----------------------------------------------------------------------------
/**
 *	@brief	抵抗無効時間 スタート
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void HitWait_Start( RING_POKE* pRPoke )
{
	fx32 fxStaminaPar;
	fx32 fxWait;

	// スタミナパーセントを算出
	fxStaminaPar = FX_Div(
			pRPoke->DropPoke->fxStamina,
			pRPoke->DropPoke->cpp->fxStaminaMax );

	fxWait = ( ( FX32_CONST(1) - fxStaminaPar ) * 10 ) - 3;

	// 0以下補正
	if( fxWait < 0 ){ fxWait = 0; }

	pRPoke->HitWait = fxWait >> FX32_SHIFT;

	Poke_SetState( pRPoke, POKE_ST_HIT_WAIT );

	HOSAKA_PRINT("[%d,%d] Hit Wait In : %d \n", pRPoke->NetID, pRPoke->PokeID, pRPoke->HitWait );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	落下 スタート
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Fall_Start( RING_POKE* pRPoke )
{
	pRPoke->FallFrame = POKE_FALL_TIMER;
	pRPoke->HitWait = 0;

	// ポインタがNULLなら自殺点
	if( pRPoke->PushPoke == NULL )
	{
		pRPoke->Score = MATH_IMax( pRPoke->Score - SCORE_DEC_SUICIDE, 0 );
		PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_OWN, 1 );
		HOSAKA_PRINT("[%d] Poke Fall by self.\n", pRPoke->NetID);
	}
	else
	{
		RING_POKE* poke_ptr = pRPoke->PushPoke;

		HOSAKA_PRINT("[%d] Poke Fall by %d.\n", pRPoke->NetID, poke_ptr->NetID );

		poke_ptr->Score = MATH_IMin( poke_ptr->Score + SCORE_FALL, DROP_SCORE_MAX );
		// AP:ポイント獲得回数
		PTFrame_AddActionPoint( poke_ptr->work, poke_ptr->NetID, poke_ptr->PokeID, ADD_TYPE_GET, 1 );

		pRPoke->PushPoke = NULL;	///< 誰に押されたか忘れる
	}

	// AP:落ちた回数
	PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_FALL, 1 );
	// AP:失敗回数
	PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_FAIL, 1 );

	Poke_SetState( pRPoke, POKE_ST_FALL );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	交代 スタート
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Change_Start( RING_POKE* pRPoke )
{
	GF_ASSERT( pRPoke->bChange == 0 );
	GF_ASSERT( pRPoke->bPanic == 0 );

	pRPoke->bChange = 1;
	pRPoke->ChgState = CHG_BALL_UP;
	pRPoke->LastPos = pRPoke->NowPos;
	// AP:交代
	PTFrame_AddActionPoint( pRPoke->work, pRPoke->NetID, pRPoke->PokeID, ADD_TYPE_CHANGE, 1 );

	HOSAKA_PRINT("[%d] Change IN \n", pRPoke->NetID);
}

//-----------------------------------------------------------------------------
/**
 *	@brief	スタミナを減算
 *
 *	@param	RING_POKE* pRPoke
 *	@param	fxDec	減らす値
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void Stamina_Dec( RING_POKE* pRPoke, fx32 fxDec )
{
	pRPoke->DropPoke->fxStamina -= fxDec;
	if( pRPoke->DropPoke->fxStamina < 0 ){ pRPoke->DropPoke->fxStamina = 0; }
	HOSAKA_PRINT("[%d,%d] stamina dec! \n",pRPoke->NetID, pRPoke->PokeID);
}



//-----------------------------------------------------------------------------
/**
 *	@brief	コンバートデータを取得
 *
 *	@param	work
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void SetDropPokeParam( PKTHLN_PTR work )
{
	int i, j;
	DROP_CONV_DATA ConvData = {0};

	ArchiveDataLoad( &ConvData, ARC_PKTHEVT_PARAM, NARC_pkthln_evt_info_drop_bin );

	for( i=0; i<ENTRY_MEMBER_NUM_MAX; i++ )
	{
		for( j=0; j<ENTRY_MAX; j++ )
		{
			const POKE_SPEC* pPokeSpec;
			DROP_POKE_PARAM* pDropPokeParam;

			pPokeSpec = PTFrame_GetPokeSpec( work, i, j );
			pDropPokeParam = GetDropPokeParam( work, i, j );

			GF_ASSERT( pPokeSpec->Power < DROP_CONV_DATA_MAX );
			GF_ASSERT( pPokeSpec->Stamina < DROP_CONV_DATA_MAX );
			GF_ASSERT( pPokeSpec->Speed < DROP_CONV_DATA_MAX );
			GF_ASSERT( pPokeSpec->Jump < DROP_CONV_DATA_MAX );

			pDropPokeParam->fxPush = FX32_CONST( ConvData.Push[ pPokeSpec->Power ] ) / 10;				///< 1/10にして使用
			pDropPokeParam->fxPower = FX32_CONST( ConvData.Power[ pPokeSpec->Power ] ) / 10;			///< 1/10にして使用
			pDropPokeParam->fxStaminaMax = FX32_CONST( ConvData.Stamina[ pPokeSpec->Stamina ] );
			pDropPokeParam->fxMove = FX32_CONST( ConvData.Speed[ pPokeSpec->Speed ] ) / 10;				///< 1/10にして使用
			pDropPokeParam->fxPressPush = FX32_CONST( ConvData.PressPower[ pPokeSpec->Jump ] ) / 10;	///< 1/10にして使用
			pDropPokeParam->fxPressPower = FX32_CONST( ConvData.PressPower[ pPokeSpec->Jump ] ) / 10;	///< 1/10にして使用
			pDropPokeParam->JumpLevel = pPokeSpec->Jump;

			HOSAKA_PRINT( "[%d,%d] Push:%03f Power:%03f fxStaminaMax:%d Move:%03f PressPush:%03f PressPower:%03f \n",
				i, j,
				FX_FX32_TO_F32(pDropPokeParam->fxPush),
				FX_FX32_TO_F32(pDropPokeParam->fxPower),
				pDropPokeParam->fxStaminaMax >> FX32_SHIFT,
				FX_FX32_TO_F32(pDropPokeParam->fxMove),
				FX_FX32_TO_F32(pDropPokeParam->fxPressPush),
				FX_FX32_TO_F32(pDropPokeParam->fxPressPower) );
		}
	}
}

//-----------------------------------------------------------------------------
/**
 *	@brief	親ポインタから各ポケモンのパラメータを取得
 *
 *	@param	PKTHLN_PTR work
 *	@param	NetID
 *	@param	PokeIdx
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static DROP_POKE_PARAM* GetDropPokeParam( PKTHLN_PTR work, u8 NetID, u8 PokeIdx )
{
	DROP_POKE_PARAM* pRet;
	PKTHLN_DROP_PTR ptr = PTFrame_GetEventWorkPtr(work);

	GF_ASSERT(ptr);
	GF_ASSERT( NetID < ENTRY_MEMBER_NUM_MAX );
	GF_ASSERT( PokeIdx < ENTRY_MAX );

	pRet = &ptr->DropPokeParam[ NetID * ENTRY_MAX + PokeIdx ];

	GF_ASSERT( pRet );

	return pRet;
}

//-----------------------------------------------------------------------------
/**
 *	@brief	次のポケモンを参照先に設定
 *
 *	@param	RING_POKE* pRPoke
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PokeID_Add( RING_POKE* pRPoke )
{
	// 参照ポケモン変更
	pRPoke->PokeID = (pRPoke->PokeID+1) % ENTRY_MAX;
	pRPoke->DropPoke = &pRPoke->TeamData->DropPoke[ pRPoke->PokeID ];
}

//-----------------------------------------------------------------------------
/**
 *	@brief	効果音再生(+プレイヤー切替)
 *
 *	@param	int inSE
 *	@param	inMine
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void PlaySe( int inSE, BOOL inMine )
{
	int se_player;

	if(inMine){
		se_player = SE_PLAYER_MINE;
	}else{
		se_player = SE_PLAYER_OTHERS;
	}
	Snd_SePlayEx( inSE, se_player );
}

//-----------------------------------------------------------------------------
/**
 *	@brief	BMPWIN作成
 *
 *	@param	PKTHLN_DROP_PTR ptr
 *
 *	@retval
 */
//-----------------------------------------------------------------------------
static void CreateBmpWin( PKTHLN_DROP_PTR ptr )
{
	enum
	{
		WIN_CGX_BASE = 1,
		WIN_REC_CGX = WIN_CGX_BASE,
	};

	//----------レコードＢＭＰ---------------
	//ＢＮＰウィンドウデータ
	static const BMPWIN_DAT c_RecordBmpData = {
			BG_FRM_M_RSLT, WIN_REC_PX, WIN_REC_PY,
			WIN_REC_SX, WIN_REC_SY, MAIN_FONT_PAL, WIN_REC_CGX
	};

	GF_BGL_BmpWinAddEx( ptr->bgl, &ptr->Win, &c_RecordBmpData );
}

//----------------------------------------------------------------------------
/**
 *	@brief		レコード文字列展開
 *
 *	@param	work
 *	@param	inPoint
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void SetRecordStr( PKTHLN_PTR work, const int inPoint )
{
	PKTHLN_DROP_PTR ptr;
	WORDSET *word;
	MSGDATA_MANAGER *msgman;
	STRBUF * str;
	int integer;

	ptr = PTFrame_GetEventWorkPtr(work);
    word = ptr->WordSet;
	msgman = ptr->MsgMan;
	integer = inPoint;	//整数部分

	GF_BGL_BmpWinDataFill( &ptr->Win, 0);

	WORDSET_RegisterNumber(word, 0, integer, 3, NUMBER_DISPTYPE_LEFT, NUMBER_CODETYPE_DEFAULT);

    str = MSGDAT_UTIL_AllocExpandString(word, msgman, msg_pkthln_new_record08, ptr->HeapID);

	GF_STR_PrintColor( &ptr->Win, FONT_TOUCH, str, 0, 0, MSG_NO_PUT, COL_N_WHITE, NULL );

	STRBUF_Delete( str );//<一括表示なので、メモリをここで消してもOK

	GF_BGL_BmpWinOn(&ptr->Win);
}

//----------------------------------------------------------------------------
/**
 *	@brief	交代ボタンステート決定
 *
 *	@param	ptr				競技ポインタ
 *	@param	TpState			タッチ情報
 *	@param	inValid			ボタン矩形か？
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void SetBtnState( RING_POKE* pRPoke, const TP_STATE_DROP* TpState, const BOOL bValid )
{
	u8 *btn_st;
	u8 st;
	BOOL bSweat;

	btn_st = &pRPoke->BtnSt;
	st = BTN_ST_PUSH_OK;
	bSweat = ( ( pRPoke->DropPoke->fxStamina >> FX32_SHIFT) < STAMINA_SWEAT_POINT );

	// 交代可能か?
	if (Poke_CheckChangeEnable(pRPoke)){
		// 催促チェック
		if (bSweat){
			st = BTN_ST_PUSH_OK_FLASH;
		}else{
			st = BTN_ST_PUSH_OK;
		}
	}else{
		st = BTN_ST_PUSH_NG;
	}

	if (!bValid){
		// 現在のステートで表示
		*btn_st = st;
		return;
	}

	if ( TpState->Trg ){
		//現在のボタンステートを確認して押下ステートに移行
		if ((st == BTN_ST_PUSH_OK)||(st == BTN_ST_PUSH_OK_FLASH)){
			*btn_st = BTN_ST_PUSH_OK_PUSH;
		}else if(st == BTN_ST_PUSH_NG){
			*btn_st = BTN_ST_PUSH_NG_PUSH;
		}else{
			GF_ASSERT(0);
		}
	}else if ( TpState->Cont ){	//ベタ
		//ステートが変わっても、リリースするまでは同じ見た目を保持する(つまり何もしない)
	}else{	//リリース
		//現在のステートで表示
		*btn_st = st;
	}
}
